Exemplo n.º 1
0
        /// <summary>
        /// Creates a text ATOM value.
        /// </summary>
        /// <param name="textValue">The text value to use.</param>
        /// <param name="contentKind">The content kind of the value.</param>
        /// <returns>The Atom text value.</returns>
        private static AtomTextConstruct CreateAtomTextConstruct(string textValue, SyndicationTextContentKind contentKind)
        {
            AtomTextConstructKind kind;

            switch (contentKind)
            {
            case SyndicationTextContentKind.Plaintext:
                kind = AtomTextConstructKind.Text;
                break;

            case SyndicationTextContentKind.Html:
                kind = AtomTextConstructKind.Html;
                break;

            case SyndicationTextContentKind.Xhtml:
                kind = AtomTextConstructKind.Xhtml;
                break;

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_CreateAtomTextConstruct));
            }

            return(new AtomTextConstruct
            {
                Kind = kind,
                Text = textValue,
            });
        }
Exemplo n.º 2
0
        /// <summary>
        /// Gets the correct set of function signatures for type promotion for a given binary operator.
        /// </summary>
        /// <param name="operatorKind">The operator kind to get the signatures for.</param>
        /// <returns>The set of signatures for the specified <paramref name="operatorKind"/>.</returns>
        private static FunctionSignature[] GetFunctionSignatures(BinaryOperatorKind operatorKind)
        {
            switch (operatorKind)
            {
            case BinaryOperatorKind.Or:         // fall through
            case BinaryOperatorKind.And:
                return(logicalSignatures);

            case BinaryOperatorKind.Equal:                  // fall through
            case BinaryOperatorKind.NotEqual:               // fall through
            case BinaryOperatorKind.GreaterThan:            // fall through
            case BinaryOperatorKind.GreaterThanOrEqual:     // fall through
            case BinaryOperatorKind.LessThan:               // fall through
            case BinaryOperatorKind.LessThanOrEqual:
                return(relationalSignatures);

            case BinaryOperatorKind.Add:                    // fall through
            case BinaryOperatorKind.Subtract:               // fall through
            case BinaryOperatorKind.Multiply:               // fall through
            case BinaryOperatorKind.Divide:                 // fall through
            case BinaryOperatorKind.Modulo:                 // fall through
                return(arithmeticSignatures);

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Binary_UnreachableCodepath));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Compute the result type of a binary operator based on the type of its operands and the operator kind.
        /// </summary>
        /// <param name="typeReference">The type reference of the operators.</param>
        /// <param name="operatorKind">The kind of operator.</param>
        /// <returns>The result type reference of the binary operator.</returns>
        internal static IEdmPrimitiveTypeReference GetBinaryOperatorResultType(IEdmPrimitiveTypeReference typeReference, BinaryOperatorKind operatorKind)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(typeReference != null, "type != null");

            switch (operatorKind)
            {
            case BinaryOperatorKind.Or:                     // fall through
            case BinaryOperatorKind.And:                    // fall through
            case BinaryOperatorKind.Equal:                  // fall through
            case BinaryOperatorKind.NotEqual:               // fall through
            case BinaryOperatorKind.GreaterThan:            // fall through
            case BinaryOperatorKind.GreaterThanOrEqual:     // fall through
            case BinaryOperatorKind.LessThan:               // fall through
            case BinaryOperatorKind.LessThanOrEqual:
                return(EdmCoreModel.Instance.GetBoolean(typeReference.IsNullable));

            case BinaryOperatorKind.Add:            // fall through
            case BinaryOperatorKind.Subtract:       // fall through
            case BinaryOperatorKind.Multiply:       // fall through
            case BinaryOperatorKind.Divide:         // fall through
            case BinaryOperatorKind.Modulo:
                return(typeReference);

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.QueryNodeUtils_BinaryOperatorResultType_UnreachableCodepath));
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Reads EPM values from a person construct (author or contributor).
        /// </summary>
        /// <param name="targetList">The target list, this can be either a list of properties (on entry or complex value),
        /// or a list of items (for a collection of primitive types).</param>
        /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity, complex or primitive).</param>
        /// <param name="targetSegment">The target segment which points to either author or contributor element.</param>
        /// <param name="personMetadata">The person ATOM metadata to read from.</param>
        private void ReadPersonEpm(ReadOnlyEnumerable <ODataProperty> targetList, IEdmTypeReference targetTypeReference, EpmTargetPathSegment targetSegment, AtomPersonMetadata personMetadata)
        {
            Debug.Assert(targetList != null, "targetList != null");
            Debug.Assert(targetTypeReference != null, "targetTypeReference != null");
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(
                targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName,
                "targetSegment must be author or contributor.");
            Debug.Assert(personMetadata != null, "personMetadata != null");

            foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments)
            {
                Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author.");
                Debug.Assert(
                    subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty,
                    "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM.");
                switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorName:
                case SyndicationItemProperty.ContributorName:
                    // Note that person name can never specify true null in EPM, since it can't have the m:null attribute on it.
                    string personName = personMetadata.Name;
                    if (personName != null)
                    {
                        this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personName);
                    }

                    break;

                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.ContributorEmail:
                    string personEmail = personMetadata.Email;
                    if (personEmail != null)
                    {
                        this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personEmail);
                    }

                    break;

                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorUri:
                    string personUri = personMetadata.UriFromEpm;
                    if (personUri != null)
                    {
                        this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personUri);
                    }

                    break;

                default:
                    // Unhandled EpmTargetPathSegment.SegmentName.
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadPersonEpm));
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Gets the payload type name for an OData OM instance for JsonLight.
        /// </summary>
        /// <param name="payloadItem">The payload item to get the type name for.</param>
        /// <returns>The type name as read from the payload item (or constructed for primitive items).</returns>
        internal static string GetPayloadTypeName(object payloadItem)
        {
            DebugUtils.CheckNoExternalCallers();

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

            TypeCode typeCode = ODataPlatformHelper.GetTypeCode(payloadItem.GetType());

            switch (typeCode)
            {
            // In JSON only boolean, DateTime, String, Int32 and Double are recognized as primitive types
            // (without additional type conversion). So only check for those; if not one of these primitive
            // types it must be a complex, entity or collection value.
            case TypeCode.Boolean: return(Metadata.EdmConstants.EdmBooleanTypeName);

            case TypeCode.DateTime: return(Metadata.EdmConstants.EdmDateTimeTypeName);

            case TypeCode.String: return(Metadata.EdmConstants.EdmStringTypeName);

            case TypeCode.Int32: return(Metadata.EdmConstants.EdmInt32TypeName);

            case TypeCode.Double: return(Metadata.EdmConstants.EdmDoubleTypeName);

            default:
                Debug.Assert(typeCode == TypeCode.Object, "If not one of the primitive types above, it must be an object in JSON.");
                break;
            }

            ODataComplexValue complexValue = payloadItem as ODataComplexValue;

            if (complexValue != null)
            {
                return(complexValue.TypeName);
            }

            ODataCollectionValue collectionValue = payloadItem as ODataCollectionValue;

            if (collectionValue != null)
            {
                return(collectionValue.TypeName);
            }

            ODataEntry entry = payloadItem as ODataEntry;

            if (entry != null)
            {
                return(entry.TypeName);
            }

            throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightReader_ReadEntryStart));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Gets the correct set of function signatures for type promotion for a given binary operator.
        /// </summary>
        /// <param name="operatorKind">The operator kind to get the signatures for.</param>
        /// <returns>The set of signatures for the specified <paramref name="operatorKind"/>.</returns>
        private static FunctionSignature[] GetFunctionSignatures(UnaryOperatorKind operatorKind)
        {
            switch (operatorKind)
            {
            case UnaryOperatorKind.Negate:
                return(negationSignatures);

            case UnaryOperatorKind.Not:
                return(notSignatures);

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Unary_UnreachableCodepath));
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Reads the end of a collection; this includes collection-level instance annotations.
        /// </summary>
        /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param>
        /// <remarks>
        /// Pre-Condition:  EndArray node:      End of the collection content array
        /// Post-Condition: EndOfInput:         All of the collection payload has been consumed.
        /// </remarks>
        internal void ReadCollectionEnd(bool isReadingNestedPayload)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "Pre-condition: JsonNodeType.EndArray");
            this.JsonReader.AssertNotBuffering();

            this.JsonReader.ReadEndArray();

            if (!isReadingNestedPayload)
            {
                // Create a new duplicate property names checker object here; we don't have to use the one from reading the
                // collection start since we don't allow any annotations/properties after the collection property.
                DuplicatePropertyNamesChecker collectionEndDuplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();

                // Fail on anything after the collection that is not a custom instance annotation
                while (this.JsonReader.NodeType == JsonNodeType.Property)
                {
                    this.ProcessProperty(
                        collectionEndDuplicatePropertyNamesChecker,
                        this.ReadTypePropertyAnnotationValue,
                        (propertyParsingResult, propertyName) =>
                    {
                        // This method will allow and skip over any custom annotations, but will not report them as enum values, so any result we get other than EndOfObject indicates a malformed payload.
                        switch (propertyParsingResult)
                        {
                        case PropertyParsingResult.CustomInstanceAnnotation:
                            this.JsonReader.SkipValue();
                            break;

                        case PropertyParsingResult.ODataInstanceAnnotation:             // fall through
                        case PropertyParsingResult.PropertyWithoutValue:                // fall through
                        case PropertyParsingResult.PropertyWithValue:                   // fall through
                        case PropertyParsingResult.MetadataReferenceProperty:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName));

                        case PropertyParsingResult.EndOfObject:
                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionEnd));
                        }
                    });
                }

                // read the end-object node of the value containing the 'value' property
                this.JsonReader.ReadEndObject();
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Parses the eq, ne, lt, gt, le, ge operators.
        /// </summary>
        /// <returns>The lexical token representing the expression.</returns>
        private QueryToken ParseComparison()
        {
            this.RecurseEnter();
            QueryToken left = this.ParseAdditive();

            while (this.lexer.CurrentToken.IsComparisonOperator)
            {
                BinaryOperatorKind binaryOperatorKind;
                switch (this.lexer.CurrentToken.Text)
                {
                case ExpressionConstants.KeywordEqual:
                    binaryOperatorKind = BinaryOperatorKind.Equal;
                    break;

                case ExpressionConstants.KeywordNotEqual:
                    binaryOperatorKind = BinaryOperatorKind.NotEqual;
                    break;

                case ExpressionConstants.KeywordGreaterThan:
                    binaryOperatorKind = BinaryOperatorKind.GreaterThan;
                    break;

                case ExpressionConstants.KeywordGreaterThanOrEqual:
                    binaryOperatorKind = BinaryOperatorKind.GreaterThanOrEqual;
                    break;

                case ExpressionConstants.KeywordLessThan:
                    binaryOperatorKind = BinaryOperatorKind.LessThan;
                    break;

                case ExpressionConstants.KeywordLessThanOrEqual:
                    binaryOperatorKind = BinaryOperatorKind.LessThanOrEqual;
                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriQueryExpressionParser_ParseComparison));
                }

                this.lexer.NextToken();
                QueryToken right = this.ParseAdditive();
                left = new BinaryOperatorToken(binaryOperatorKind, left, right);
            }

            this.RecurseLeave();
            return(left);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Writes a non-leaf segment which has sub segments.
        /// </summary>
        /// <param name="targetSegment">The segment being written</param>
        /// <param name="epmValueCache">EPM value cache to use to get property values, or a primitive value</param>
        /// <param name="typeReference">The type of the entry or collection item.</param>
        private void WriteParentSegment(EpmTargetPathSegment targetSegment, object epmValueCache, IEdmTypeReference typeReference)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");

            if (targetSegment.SegmentName == AtomConstants.AtomAuthorElementName)
            {
                AtomPersonMetadata authorMetadata = this.WritePersonEpm(targetSegment, epmValueCache, typeReference);

                if (authorMetadata != null)
                {
                    List <AtomPersonMetadata> authors = (List <AtomPersonMetadata>) this.entryMetadata.Authors;
                    if (authors == null)
                    {
                        authors = new List <AtomPersonMetadata>();
                        this.entryMetadata.Authors = authors;
                    }

                    authors.Add(authorMetadata);
                }
            }
            else if (targetSegment.SegmentName == AtomConstants.AtomContributorElementName)
            {
                AtomPersonMetadata contributorMetadata = this.WritePersonEpm(targetSegment, epmValueCache, typeReference);

                if (contributorMetadata != null)
                {
                    List <AtomPersonMetadata> contributors = (List <AtomPersonMetadata>) this.entryMetadata.Contributors;
                    if (contributors == null)
                    {
                        contributors = new List <AtomPersonMetadata>();
                        this.entryMetadata.Contributors = contributors;
                    }

                    contributors.Add(contributorMetadata);
                }
            }
            else
            {
                // Unhandled EpmTargetPathSegment.SegmentName.
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteParentSegment_TargetSegmentName));
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Write the unary token as URI part to this builder.
        /// </summary>
        /// <param name="unary">To write as URI part.</param>
        private void WriteUnary(UnaryOperatorToken unary)
        {
            ExceptionUtils.CheckArgumentNotNull(unary, "unary");

            switch (unary.OperatorKind)
            {
            case UnaryOperatorKind.Negate:
                this.builder.Append(ExpressionConstants.SymbolNegate);
                break;

            case UnaryOperatorKind.Not:
                this.builder.Append(ExpressionConstants.KeywordNot);
                this.builder.Append(ExpressionConstants.SymbolEscapedSpace);
                break;

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataUriBuilder_WriteUnary_UnreachableCodePath));
            }

            this.WriteQuery(unary.Operand);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Reads a non-leaf segment which has sub segments.
        /// </summary>
        /// <param name="targetSegment">The segment being read.</param>
        /// <param name="entryMetadata">The ATOM entry metadata to read from.</param>
        private void ReadParentSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(entryMetadata != null, "entryMetadata != null");
            switch (targetSegment.SegmentName)
            {
            case AtomConstants.AtomAuthorElementName:
                // If a singleton property (non-collection) is mapped to author and there are multiple authors
                // EPM uses the first author.
                AtomPersonMetadata authorMetadata = entryMetadata.Authors.FirstOrDefault();
                if (authorMetadata != null)
                {
                    this.ReadPersonEpm(
                        this.EntryState.Entry.Properties.ToReadOnlyEnumerable("Properties"),
                        this.EntryState.EntityType.ToTypeReference(),
                        targetSegment,
                        authorMetadata);
                }

                break;

            case AtomConstants.AtomContributorElementName:
                // If a singleton property (non-collection) is mapped to contributor and there are multiple contributors
                // EPM uses the first contributor.
                AtomPersonMetadata contributorMetadata = entryMetadata.Contributors.FirstOrDefault();
                if (contributorMetadata != null)
                {
                    this.ReadPersonEpm(
                        this.EntryState.Entry.Properties.ToReadOnlyEnumerable("Properties"),
                        this.EntryState.EntityType.ToTypeReference(),
                        targetSegment,
                        contributorMetadata);
                }

                break;

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadParentSegment_TargetSegmentName));
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Reads a primitive, complex or collection value.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the property value.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use - if null the method should create a new one if necessary.</param>
        /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param>
        /// <param name="validateNullValue">true to validate null values; otherwise false.</param>
        /// <param name="propertyName">The name of the property whose value is being read, if applicable.</param>
        /// <returns>The value of the property read.</returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.PrimitiveValue   - the value of the property is a primitive value
        ///                 JsonNodeType.StartObject      - the value of the property is an object
        ///                 JsonNodeType.StartArray       - the value of the property is an array - method will fail in this case.
        /// Post-Condition: almost anything - the node after the property value.
        ///
        /// Returns the value of the property read, which can be one of:
        /// - null
        /// - primitive value
        /// - <see cref="ODataComplexValue"/>
        /// - <see cref="ODataCollectionValue"/>
        /// </remarks>
        private object ReadNonEntityValueImplementation(
            IEdmTypeReference expectedTypeReference,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            bool validateNullValue,
            string propertyName)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(
                this.JsonReader.NodeType == JsonNodeType.PrimitiveValue || this.JsonReader.NodeType == JsonNodeType.StartObject || this.JsonReader.NodeType == JsonNodeType.StartArray,
                "Pre-Condition: expected JsonNodeType.PrimitiveValue or JsonNodeType.StartObject or JsonNodeType.StartArray");
            Debug.Assert(
                expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(),
                "Only primitive, complex or collection types can be read by this method.");
            Debug.Assert(
                expectedTypeReference == null || collectionValidator == null,
                "If an expected value type reference is specified, no collection validator must be provided.");
            this.JsonReader.AssertNotBuffering();

            // Property values can be only primitives or objects. No property can have a JSON array value.
            JsonNodeType nodeType = this.JsonReader.NodeType;

            if (nodeType == JsonNodeType.StartArray)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_CannotReadPropertyValue(nodeType));
            }

            // Try to read a null value
            object result;

            if (ODataJsonReaderCoreUtils.TryReadNullValue(
                    this.JsonReader,
                    this.VerboseJsonInputContext,
                    expectedTypeReference,
                    validateNullValue,
                    propertyName))
            {
                result = null;
            }
            else
            {
                // Read the payload type name
                string payloadTypeName = this.FindTypeNameInPayload();

                SerializationTypeNameAnnotation serializationTypeNameAnnotation;
                EdmTypeKind       targetTypeKind;
                IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                    EdmTypeKind.None,
                    /*defaultPrimitivePayloadType*/ null,
                    expectedTypeReference,
                    payloadTypeName,
                    this.Model,
                    this.MessageReaderSettings,
                    this.Version,
                    this.GetNonEntityValueKind,
                    out targetTypeKind,
                    out serializationTypeNameAnnotation);

                switch (targetTypeKind)
                {
                case EdmTypeKind.Primitive:
                    Debug.Assert(targetTypeReference == null || targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type.");
                    IEdmPrimitiveTypeReference primitiveTargetTypeReference = targetTypeReference == null ? null : targetTypeReference.AsPrimitive();
                    if (payloadTypeName != null && !primitiveTargetTypeReference.IsSpatial())
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_InvalidPrimitiveTypeName(payloadTypeName));
                    }

                    result = this.ReadPrimitiveValue(
                        primitiveTargetTypeReference,
                        validateNullValue,
                        propertyName);
                    break;

                case EdmTypeKind.Complex:
                    Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type.");
                    result = this.ReadComplexValueImplementation(
                        targetTypeReference == null ? null : targetTypeReference.AsComplex(),
                        payloadTypeName,
                        serializationTypeNameAnnotation,
                        duplicatePropertyNamesChecker);
                    break;

                case EdmTypeKind.Collection:
                    Debug.Assert(this.Version >= ODataVersion.V3, "Type resolution should already fail if we would decide to read a collection value in V1/V2 payload.");
                    IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference);
                    result = this.ReadCollectionValueImplementation(
                        collectionTypeReference,
                        payloadTypeName,
                        serializationTypeNameAnnotation);
                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataVerboseJsonPropertyAndValueDeserializer_ReadPropertyValue));
                }

                // If we have no expected type make sure the collection items are of the same kind and specify the same name.
                if (collectionValidator != null)
                {
                    string payloadTypeNameFromResult = ODataVerboseJsonReaderUtils.GetPayloadTypeName(result);
                    Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference.");
                    collectionValidator.ValidateCollectionItem(payloadTypeNameFromResult, targetTypeKind);
                }
            }

            this.JsonReader.AssertNotBuffering();
            return(result);
        }
Exemplo n.º 13
0
        private void SetEpmValueForSegment(
            EntityPropertyMappingInfo epmInfo,
            int propertyValuePathIndex,
            IEdmStructuredTypeReference segmentStructuralTypeReference,
            ReadOnlyEnumerable <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.");

            // TODO: Server seems to have a bug where if there's an EPM for an open property where the EPM uses complex types (the source path is deeper than 1)
            // then it will actually materialize the property as entry level string property, with the value of the deep property value. If there are multiple deep
            // EPM for the same top-level open property it seems to set the entry level property multiple times with the values as they come from payload.
            // Client on the other hand doesn't have open properties, and always has a type, so no problem there.
            if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1)
            {
                throw new ODataException(ODataErrorStrings.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(ODataErrorStrings.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,
                        propertyName);

                    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,
                    existingComplexValue.Properties.ToReadOnlyEnumerable("Properties"),
                    propertyValue);

                break;

            case EdmTypeKind.Collection:
                Debug.Assert(propertyType.IsNonEntityCollectionType(), "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(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
        /// <summary>
        /// Reads the primitive, complex or collection value.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the value.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param>
        /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param>
        /// <param name="validateNullValue">true to validate a null value (i.e., throw if a null value is being written for a non-nullable property); otherwise false.</param>
        /// <param name="epmPresent">Whether any EPM mappings exist.</param>
        /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param>
        /// <returns>The value read (null, primitive CLR value, ODataComplexValue or ODataCollectionValue).</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element    - The XML element containing the value to read (also the attributes will be read from it)
        /// Post-Condition:  XmlNodeType.EndElement - The end tag of the element.
        ///                  XmlNodeType.Element    - The empty element node.
        /// </remarks>
        private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool epmPresent, string propertyName)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(),
                "Only primitive, complex or collection types can be read by this method.");
            Debug.Assert(
                expectedTypeReference == null || collectionValidator == null,
                "If an expected value type reference is specified, no collection validator must be provided.");
            this.XmlReader.AssertNotBuffering();

            // Read the attributes looking for m:type and m:null
            string payloadTypeName;
            bool   isNull;

            this.ReadNonEntityValueAttributes(out payloadTypeName, out isNull);

            object result;

            if (isNull)
            {
                result = this.ReadNullValue(expectedTypeReference, validateNullValue, propertyName);
            }
            else
            {
                // If we could derive the item type name from the collection's type name and no type name was specified in the payload
                // fill it in now.
                EdmTypeKind payloadTypeKind;
                bool        derivedItemTypeNameFromCollectionTypeName = false;
                if (collectionValidator != null && payloadTypeName == null)
                {
                    payloadTypeName = collectionValidator.ItemTypeNameFromCollection;
                    payloadTypeKind = collectionValidator.ItemTypeKindFromCollection;
                    derivedItemTypeNameFromCollectionTypeName = payloadTypeKind != EdmTypeKind.None;
                }

                // Resolve the payload type name and compute the target type kind and target type reference.
                SerializationTypeNameAnnotation serializationTypeNameAnnotation;
                EdmTypeKind       targetTypeKind;
                IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                    EdmTypeKind.None,
                    /*defaultPrimitivePayloadType*/ edmStringType,
                    expectedTypeReference,
                    payloadTypeName,
                    this.Model,
                    this.MessageReaderSettings,
                    this.Version,
                    this.GetNonEntityValueKind,
                    out targetTypeKind,
                    out serializationTypeNameAnnotation);

                if (derivedItemTypeNameFromCollectionTypeName)
                {
                    Debug.Assert(
                        serializationTypeNameAnnotation == null,
                        "If we derived the item type name from the collection type name we must not have created a serialization type name annotation.");
                    serializationTypeNameAnnotation = new SerializationTypeNameAnnotation {
                        TypeName = null
                    };
                }

                // If we have no expected type make sure the collection items are of the same kind and specify the same name.
                if (collectionValidator != null)
                {
                    Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference.");
                    collectionValidator.ValidateCollectionItem(payloadTypeName, targetTypeKind);
                }

                switch (targetTypeKind)
                {
                case EdmTypeKind.Primitive:
                    Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type.");
                    result = this.ReadPrimitiveValue(targetTypeReference.AsPrimitive());
                    break;

                case EdmTypeKind.Complex:
                    Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type.");
                    result = this.ReadComplexValue(
                        targetTypeReference == null ? null : targetTypeReference.AsComplex(),
                        payloadTypeName,
                        serializationTypeNameAnnotation,
                        duplicatePropertyNamesChecker,
                        epmPresent);
                    break;

                case EdmTypeKind.Collection:
                    IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference);
                    result = this.ReadCollectionValue(
                        collectionTypeReference,
                        payloadTypeName,
                        serializationTypeNameAnnotation);
                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue));
                }
            }

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            this.XmlReader.AssertNotBuffering();
            return(result);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Returns the 4 bits that correspond to the specified character.
        /// </summary>
        /// <param name="c">Character in the 0-F range to be converted.</param>
        /// <returns>The 4 bits that correspond to the specified character.</returns>
        /// <exception cref="FormatException">Thrown when 'c' is not in the '0'-'9','a'-'f' range.</exception>
        /// <remarks>This is a copy of WebConvert.HexCharToNibble.</remarks>
        private static byte HexCharToNibble(char c)
        {
            Debug.Assert(IsCharHexDigit(c), string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} is not a hex digit.", c));

            switch (c)
            {
            case '0':
                return(0);

            case '1':
                return(1);

            case '2':
                return(2);

            case '3':
                return(3);

            case '4':
                return(4);

            case '5':
                return(5);

            case '6':
                return(6);

            case '7':
                return(7);

            case '8':
                return(8);

            case '9':
                return(9);

            case 'a':
            case 'A':
                return(10);

            case 'b':
            case 'B':
                return(11);

            case 'c':
            case 'C':
                return(12);

            case 'd':
            case 'D':
                return(13);

            case 'e':
            case 'E':
                return(14);

            case 'f':
            case 'F':
                return(15);

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_HexCharToNibble));
            }
        }
Exemplo n.º 16
0
        internal static bool TryUriStringToPrimitive(string text, IEdmTypeReference targetType, out object targetValue)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(text != null, "text != null");
            Debug.Assert(targetType != null, "targetType != null");

            if (targetType.IsNullable)
            {
                if (text == ExpressionConstants.KeywordNull)
                {
                    targetValue = null;
                    return(true);
                }
            }

            IEdmPrimitiveTypeReference primitiveTargetType = targetType.AsPrimitiveOrNull();

            if (primitiveTargetType == null)
            {
                targetValue = null;
                return(false);
            }

            EdmPrimitiveTypeKind targetTypeKind = primitiveTargetType.PrimitiveKind();

            byte[] byteArrayValue;
            bool   binaryResult = TryUriStringToByteArray(text, out byteArrayValue);

            if (targetTypeKind == EdmPrimitiveTypeKind.Binary)
            {
                targetValue = (object)byteArrayValue;
                return(binaryResult);
            }
            else if (binaryResult)
            {
                string keyValue = Encoding.UTF8.GetString(byteArrayValue, 0, byteArrayValue.Length);
                return(TryUriStringToPrimitive(keyValue, targetType, out targetValue));
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Guid)
            {
                Guid guidValue;
                bool result = TryUriStringToGuid(text, out guidValue);
                targetValue = guidValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.DateTime)
            {
                DateTime dateTimeValue;
                bool     result = TryUriStringToDateTime(text, out dateTimeValue);
                targetValue = dateTimeValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.DateTimeOffset)
            {
                DateTimeOffset dateTimeOffsetValue;
                bool           result = TryUriStringToDateTimeOffset(text, out dateTimeOffsetValue);
                targetValue = dateTimeOffsetValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Time)
            {
                TimeSpan timespanValue;
                bool     result = TryUriStringToTime(text, out timespanValue);
                targetValue = timespanValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Geography)
            {
                Geography geographyValue;
                bool      result = TryUriStringToGeography(text, out geographyValue);
                targetValue = geographyValue;
                return(result);
            }
            else if (targetTypeKind == EdmPrimitiveTypeKind.Geometry)
            {
                Geometry geometryValue;
                bool     result = TryUriStringToGeometry(text, out geometryValue);
                targetValue = geometryValue;
                return(result);
            }

            bool quoted = targetTypeKind == EdmPrimitiveTypeKind.String;

            if (quoted != IsUriValueQuoted(text))
            {
                targetValue = null;
                return(false);
            }

            if (quoted)
            {
                text = RemoveQuotes(text);
            }

            try
            {
                switch (targetTypeKind)
                {
                case EdmPrimitiveTypeKind.String:
                    targetValue = text;
                    break;

                case EdmPrimitiveTypeKind.Boolean:
                    targetValue = XmlConvert.ToBoolean(text);
                    break;

                case EdmPrimitiveTypeKind.Byte:
                    targetValue = XmlConvert.ToByte(text);
                    break;

                case EdmPrimitiveTypeKind.SByte:
                    targetValue = XmlConvert.ToSByte(text);
                    break;

                case EdmPrimitiveTypeKind.Int16:
                    targetValue = XmlConvert.ToInt16(text);
                    break;

                case EdmPrimitiveTypeKind.Int32:
                    targetValue = XmlConvert.ToInt32(text);
                    break;

                case EdmPrimitiveTypeKind.Int64:
                    if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixInt64, ref text))
                    {
                        targetValue = XmlConvert.ToInt64(text);
                    }
                    else
                    {
                        targetValue = default(Int64);
                        return(false);
                    }

                    break;

                case EdmPrimitiveTypeKind.Single:
                    if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixSingle, ref text))
                    {
                        targetValue = XmlConvert.ToSingle(text);
                    }
                    else
                    {
                        targetValue = default(Single);
                        return(false);
                    }

                    break;

                case EdmPrimitiveTypeKind.Double:
                    TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDouble, ref text);
                    targetValue = XmlConvert.ToDouble(text);
                    break;

                case EdmPrimitiveTypeKind.Decimal:
                    if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDecimal, ref text))
                    {
                        try
                        {
                            targetValue = XmlConvert.ToDecimal(text);
                        }
                        catch (FormatException)
                        {
                            // we need to support exponential format for decimals since we used to support them in V1
                            decimal result;
                            if (Decimal.TryParse(text, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out result))
                            {
                                targetValue = result;
                            }
                            else
                            {
                                targetValue = default(Decimal);
                                return(false);
                            }
                        }
                    }
                    else
                    {
                        targetValue = default(Decimal);
                        return(false);
                    }

                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_TryUriStringToPrimitive));
                }

                return(true);
            }
            catch (FormatException)
            {
                targetValue = null;
                return(false);
            }
            catch (OverflowException)
            {
                targetValue = null;
                return(false);
            }
        }
Exemplo n.º 17
0
        /// <summary>
        /// Read an entity reference link.
        /// </summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to check for duplicate properties and
        /// duplicate annotations; this is a separate instance per entity reference link.</param>
        /// <param name="topLevel">true if we are reading a singleton entity reference link at the top level; false if we are reading
        /// an entity reference link as part of a collection of entity reference links.</param>
        /// <returns>An instance of <see cref="ODataEntityReferenceLink"/> which was read.</returns>
        /// <remarks>
        /// Pre-Condition:  StartObject     when the entity reference link is part of a collection
        ///                 Property        the first property in the entity reference link (for a top-level link)
        ///                 EndObject       the end object node of an entity reference link (for a top-level link)
        /// Post-Condition: EndInput        for a top-level object
        ///                 EndArray        for the last link in a collection of links
        ///                 Any             for the first node of the next link in a collection of links
        /// </remarks>
        private ODataEntityReferenceLink ReadSingleEntityReferenceLink(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool topLevel)
        {
            this.JsonReader.AssertNotBuffering();

            if (!topLevel)
            {
                if (this.JsonReader.NodeType != JsonNodeType.StartObject)
                {
                    // entity reference link has to be an object
                    throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkMustBeObjectValue(this.JsonReader.NodeType));
                }

                this.JsonReader.ReadStartObject();
            }

            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            ODataEntityReferenceLink[] entityReferenceLink = { null };

            // Entity  reference links use instance annotations. Fail if we find a  property annotation.
            Func <string, object> propertyAnnotationValueReader =
                annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLink(annotationName)); };

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParsingResult, propertyName) =>
                {
                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        // Instance annotations are not allowed for entity reference links
                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName));

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        // If we find a property without a value it means that we did not find the 'url' property of the
                        // entity reference link but an invalid property annotation
                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName));

                    case PropertyParsingResult.EndOfObject:
                        break;

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                    case PropertyParsingResult.PropertyWithValue:
                        if (string.CompareOrdinal(JsonLightConstants.ODataEntityReferenceLinkUrlName, propertyName) != 0)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyInEntityReferenceLink(propertyName, JsonLightConstants.ODataEntityReferenceLinkUrlName));
                        }
                        else if (entityReferenceLink[0] != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MultipleUriPropertiesInEntityReferenceLink(JsonLightConstants.ODataEntityReferenceLinkUrlName));
                        }

                        // read the value of the 'url' property
                        string urlString = this.JsonReader.ReadStringValue(JsonLightConstants.ODataEntityReferenceLinkUrlName);
                        if (urlString == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkUrlCannotBeNull(JsonLightConstants.ODataEntityReferenceLinkUrlName));
                        }

                        entityReferenceLink[0] = new ODataEntityReferenceLink
                        {
                            Url = this.ProcessUriFromPayload(urlString)
                        };

                        ReaderValidationUtils.ValidateEntityReferenceLink(entityReferenceLink[0]);
                        break;

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadSingleEntityReferenceLink));
                    }
                });
            }

            if (entityReferenceLink[0] == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(JsonLightConstants.ODataEntityReferenceLinkUrlName));
            }

            // end of the entity reference link object
            this.JsonReader.ReadEndObject();

            this.JsonReader.AssertNotBuffering();
            return(entityReferenceLink[0]);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Reads the entity reference link instance annotations.
        /// </summary>
        /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the entity reference links scope.</param>
        /// <param name="forLinksStart">true when parsing the instance annotations before the 'value' property;
        /// false when parsing the instance annotations after the 'value' property.</param>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.Property               The first property in the payload (or the first property after the metadata URI in responses)
        ///                 JsonNodeType.EndObject              The end of the entity reference links object
        /// Post-Condition: JsonNodeType.EndObject              When the end of the entity reference links object is reached
        ///                 Any                                 The first node of the value of the 'url' property (if found)
        /// </remarks>
        private void ReadEntityReferenceLinksAnnotations(ODataEntityReferenceLinks links, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forLinksStart)
        {
            Debug.Assert(links != null, "links != null");
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);
            this.JsonReader.AssertNotBuffering();

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                // OData property annotations are not supported on entity reference links.
                Func <string, object> propertyAnnotationValueReader =
                    annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLinks); };

                bool foundValueProperty = false;
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParseResult, propertyName) =>
                {
                    switch (propertyParseResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        if (string.CompareOrdinal(ODataAnnotationNames.ODataNextLink, propertyName) == 0)
                        {
                            this.ReadEntityReferenceLinksNextLinkAnnotationValue(links);
                        }
                        else if (string.CompareOrdinal(ODataAnnotationNames.ODataCount, propertyName) == 0)
                        {
                            this.ReadEntityReferenceCountAnnotationValue(links);
                        }
                        else
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName));
                        }

                        break;

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        break;

                    case PropertyParsingResult.PropertyWithValue:
                        if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0)
                        {
                            // We did not find a supported link collection property; fail.
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidEntityReferenceLinksPropertyFound(propertyName, JsonLightConstants.ODataValuePropertyName));
                        }

                        // We found the link collection property and are done parsing property annotations;
                        foundValueProperty = true;
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        // If we find a property without a value it means that we did not find the entity reference links property (yet)
                        // but an invalid property annotation
                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyAnnotationInEntityReferenceLinks(propertyName));

                    case PropertyParsingResult.EndOfObject:
                        break;

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadEntityReferenceLinksAnnotations));
                    }
                });

                if (foundValueProperty)
                {
                    return;
                }
            }

            if (forLinksStart)
            {
                // We did not find the 'value' property.
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_ExpectedEntityReferenceLinksPropertyNotFound(JsonLightConstants.ODataValuePropertyName));
            }

            this.AssertJsonCondition(JsonNodeType.EndObject);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Reads the next parameter from the parameters payload.
        /// </summary>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker used to read a parameter payload.</param>
        /// <returns>true if a parameter was read from the payload; otherwise false.</returns>
        /// <remarks>
        /// Pre-Condition:  Property or EndObject   the property node of the parameter to read or the end object node if there are not parameters
        /// Post-Condition: Property or EndObject   the node after the property value of a primitive, complex or null collection parameter
        ///                 Any                     the start of the value representing a non-null collection parameter (the collection reader will fail if this is not a StartArray node)
        /// </remarks>
        internal bool ReadNextParameter(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            bool parameterRead = false;

            if (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                bool foundCustomInstanceAnnotation = false;
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParsingResult, parameterName) =>
                {
                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        // OData instance annotations are not supported in parameter payloads.
                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(parameterName));

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        foundCustomInstanceAnnotation = true;
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_PropertyAnnotationWithoutPropertyForParameters(parameterName));

                    case PropertyParsingResult.EndOfObject:
                        break;

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(parameterName));

                    case PropertyParsingResult.PropertyWithValue:
                        IEdmTypeReference parameterTypeReference = this.parameterReader.GetParameterTypeReference(parameterName);
                        Debug.Assert(parameterTypeReference != null, "parameterTypeReference != null");

                        ODataParameterReaderState state;
                        object parameterValue;
                        EdmTypeKind parameterTypeKind = parameterTypeReference.TypeKind();
                        switch (parameterTypeKind)
                        {
                        case EdmTypeKind.Primitive:
                            IEdmPrimitiveTypeReference primitiveTypeReference = parameterTypeReference.AsPrimitive();
                            if (primitiveTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.Stream)
                            {
                                throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedPrimitiveParameterType(parameterName, primitiveTypeReference.PrimitiveKind()));
                            }

                            parameterValue = this.ReadNonEntityValue(
                                /*payloadTypeName*/ null,
                                primitiveTypeReference,
                                /*duplicatePropertyNamesChecker*/ null,
                                /*collectionValidator*/ null,
                                /*validateNullValue*/ true,
                                /*isTopLevelPropertyValue*/ false,
                                /*insideComplexValue*/ false,
                                parameterName);
                            state = ODataParameterReaderState.Value;
                            break;

                        case EdmTypeKind.Complex:
                            parameterValue = this.ReadNonEntityValue(
                                /*payloadTypeName*/ null,
                                parameterTypeReference,
                                /*duplicatePropertyNamesChecker*/ null,
                                /*collectionValidator*/ null,
                                /*validateNullValue*/ true,
                                /*isTopLevelPropertyValue*/ false,
                                /*insideComplexValue*/ false,
                                parameterName);
                            state = ODataParameterReaderState.Value;
                            break;

#if SUPPORT_ENTITY_PARAMETER
                        case EdmTypeKind.Entity:
                            parameterValue = null;
                            state          = ODataParameterReaderState.Entry;
                            break;
#endif

                        case EdmTypeKind.Collection:
                            parameterValue = null;
                            if (this.JsonReader.NodeType == JsonNodeType.PrimitiveValue)
                            {
                                // NOTE: we support null collections in parameter payloads but nowhere else.
                                parameterValue = this.JsonReader.ReadPrimitiveValue();
                                if (parameterValue != null)
                                {
                                    throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_NullCollectionExpected(JsonNodeType.PrimitiveValue, parameterValue));
                                }

                                state = ODataParameterReaderState.Value;
                            }
                            else if (((IEdmCollectionType)parameterTypeReference.Definition).ElementType.TypeKind() == EdmTypeKind.Entity)
                            {
#if SUPPORT_ENTITY_PARAMETER
                                state = ODataParameterReaderState.Feed;
#else
                                throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedParameterTypeKind(parameterName, "Entity Collection"));
#endif
                            }
                            else
                            {
                                state = ODataParameterReaderState.Collection;
                            }

                            break;

                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedParameterTypeKind(parameterName, parameterTypeReference.TypeKind()));
                        }

                        parameterRead = true;
                        this.parameterReader.EnterScope(state, parameterName, parameterValue);
                        Debug.Assert(
                            state == ODataParameterReaderState.Collection || this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject,
                            "Expected any node for a collection; 'Property' or 'EndObject' if it is a primitive or complex value.");
                        break;

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightParameterDeserializer_ReadNextParameter));
                    }
                });

                if (foundCustomInstanceAnnotation)
                {
                    return(this.ReadNextParameter(duplicatePropertyNamesChecker));
                }
            }

            if (!parameterRead && this.JsonReader.NodeType == JsonNodeType.EndObject)
            {
                this.JsonReader.ReadEndObject();
                this.ReadPayloadEnd(/*isReadingNestedPayload*/ false);

                // Pop the scope for the previous parameter if there is any
                if (this.parameterReader.State != ODataParameterReaderState.Start)
                {
                    this.parameterReader.PopScope(this.parameterReader.State);
                }

                // Pop the 'Start' scope and enter the 'Completed' scope
                this.parameterReader.PopScope(ODataParameterReaderState.Start);
                this.parameterReader.EnterScope(ODataParameterReaderState.Completed, /*parameterName*/ null, /*parameterValue*/ null);
                this.AssertJsonCondition(JsonNodeType.EndOfInput);
            }

            return(parameterRead);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Reads a leaf segment which maps to a property value.
        /// </summary>
        /// <param name="targetSegment">The segment being read.</param>
        /// <param name="entryMetadata">The ATOM entry metadata to read from.</param>
        private void ReadPropertyValueSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(entryMetadata != null, "entryMetadata != null");

            EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;

            Debug.Assert(
                epmInfo != null && epmInfo.Attribute != null,
                "If the segment has content it must have EpmInfo which in turn must have the Epm attribute");

            switch (epmInfo.Attribute.TargetSyndicationItem)
            {
            case SyndicationItemProperty.Updated:
                // TODO: need some way to handle m:null if we decide to support it.
                if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                {
                    if (entryMetadata.UpdatedString != null)
                    {
                        this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.UpdatedString);
                    }
                }
                else if (entryMetadata.Updated.HasValue)
                {
                    this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Updated.Value));
                }

                break;

            case SyndicationItemProperty.Published:
                // TODO: need some way to handle m:null if we decide to support it.
                if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                {
                    if (entryMetadata.PublishedString != null)
                    {
                        this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.PublishedString);
                    }
                }
                else if (entryMetadata.Published.HasValue)
                {
                    this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Published.Value));
                }

                break;

            case SyndicationItemProperty.Rights:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Rights);
                break;

            case SyndicationItemProperty.Summary:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Summary);
                break;

            case SyndicationItemProperty.Title:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Title);
                break;

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadEntryEpm_ContentTarget));
            }
        }
Exemplo n.º 21
0
        /// <summary>
        /// Reads the start of a collection; this includes collection-level properties (e.g., the 'results' property) if the version permits it.
        /// </summary>
        /// <param name="collectionStartDuplicatePropertyNamesChecker">The duplicate property names checker used to keep track of the properties and annotations
        /// in the collection wrapper object.</param>
        /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param>
        /// <param name="expectedItemTypeReference">The expected item type reference or null if none is expected.</param>
        /// <param name="actualItemTypeReference">The validated actual item type reference (if specified in the payload) or the expected item type reference.</param>
        /// <returns>An <see cref="ODataCollectionStart"/> representing the collection-level information. Currently this is only the name of the collection in ATOM.</returns>
        /// <remarks>
        /// Pre-Condition:  Any:                      the start of a nested collection value; if this is not a 'StartArray' node this method will fail.
        ///                 JsonNodeType.Property:    the first property of the collection wrapper object after the metadata URI.
        ///                 JsonNodeType.EndObject:   when the collection wrapper object has no properties (other than the metadata URI).
        /// Post-Condition: JsonNodeType.StartArray:  the start of the array of the collection items.
        /// </remarks>
        internal ODataCollectionStart ReadCollectionStart(
            DuplicatePropertyNamesChecker collectionStartDuplicatePropertyNamesChecker,
            bool isReadingNestedPayload,
            IEdmTypeReference expectedItemTypeReference,
            out IEdmTypeReference actualItemTypeReference)
        {
            DebugUtils.CheckNoExternalCallers();
            this.JsonReader.AssertNotBuffering();

            actualItemTypeReference = expectedItemTypeReference;

            ODataCollectionStart collectionStart = null;

            if (isReadingNestedPayload)
            {
                Debug.Assert(!this.JsonLightInputContext.ReadingResponse, "Nested collections are only supported in parameter payloads in requests.");
                collectionStart = new ODataCollectionStart
                {
                    Name = null
                };
            }
            else
            {
                while (this.JsonReader.NodeType == JsonNodeType.Property)
                {
                    IEdmTypeReference actualItemTypeRef = expectedItemTypeReference;
                    this.ProcessProperty(
                        collectionStartDuplicatePropertyNamesChecker,
                        this.ReadTypePropertyAnnotationValue,
                        (propertyParsingResult, propertyName) =>
                    {
                        switch (propertyParsingResult)
                        {
                        case PropertyParsingResult.ODataInstanceAnnotation:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName));

                        case PropertyParsingResult.CustomInstanceAnnotation:
                            this.JsonReader.SkipValue();
                            break;

                        case PropertyParsingResult.PropertyWithoutValue:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_TopLevelPropertyAnnotationWithoutProperty(propertyName));

                        case PropertyParsingResult.PropertyWithValue:
                            if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0)
                            {
                                throw new ODataException(
                                    ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_InvalidTopLevelPropertyName(propertyName, JsonLightConstants.ODataValuePropertyName));
                            }

                            string payloadTypeName = ValidateDataPropertyTypeNameAnnotation(collectionStartDuplicatePropertyNamesChecker, propertyName);
                            if (payloadTypeName != null)
                            {
                                string itemTypeName = EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName);
                                if (itemTypeName == null)
                                {
                                    throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_InvalidCollectionTypeName(payloadTypeName));
                                }

                                EdmTypeKind targetTypeKind;
                                SerializationTypeNameAnnotation serializationTypeNameAnnotation;
                                Func <EdmTypeKind> typeKindPeekedFromPayloadFunc = () => { throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart_TypeKindFromPayloadFunc)); };
                                actualItemTypeRef = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                                    EdmTypeKind.None,
                                    /*defaultPrimitivePayloadType*/ null,
                                    expectedItemTypeReference,
                                    itemTypeName,
                                    this.Model,
                                    this.MessageReaderSettings,
                                    this.Version,
                                    typeKindPeekedFromPayloadFunc,
                                    out targetTypeKind,
                                    out serializationTypeNameAnnotation);
                            }

                            collectionStart = new ODataCollectionStart
                            {
                                Name = null
                            };

                            break;

                        case PropertyParsingResult.EndOfObject:
                            break;

                        case PropertyParsingResult.MetadataReferenceProperty:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                        default:
                            throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart));
                        }
                    });

                    actualItemTypeReference = actualItemTypeRef;
                }

                if (collectionStart == null)
                {
                    // No collection property found; there should be exactly one property in the collection wrapper that does not have a reserved name.
                    throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_ExpectedCollectionPropertyNotFound(JsonLightConstants.ODataValuePropertyName));
                }
            }

            // at this point the reader is positioned on the start array node for the collection contents
            if (this.JsonReader.NodeType != JsonNodeType.StartArray)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionContentStart(this.JsonReader.NodeType));
            }

            this.JsonReader.AssertNotBuffering();

            return(collectionStart);
        }
Exemplo n.º 22
0
        /// <summary>
        /// Writes EPM value to a person construct (author or contributor).
        /// </summary>
        /// <param name="targetSegment">The target segment which points to either author or contributor element.</param>
        /// <param name="epmValueCache">EPM value cache to use to get property values, or a primitive value</param>
        /// <param name="typeReference">The type of the entry or collection item.</param>
        /// <returns>The person metadata or null if no person metadata should be written for this mapping.</returns>
        private AtomPersonMetadata WritePersonEpm(EpmTargetPathSegment targetSegment, object epmValueCache, IEdmTypeReference typeReference)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(
                targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName,
                "targetSegment must be author or contributor.");

            AtomPersonMetadata personMetadata = null;

            foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments)
            {
                Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author.");
                Debug.Assert(
                    subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty,
                    "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM.");
                string textPropertyValue = this.GetPropertyValueAsText(subSegment, epmValueCache, typeReference);

                if (textPropertyValue == null)
                {
                    // In V2 we write the mapped properties always in-content when the value is null.
                    continue;
                }

                // Initialize the person element only if we actually need to write something to it.
                Debug.Assert(subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null, "The author subsegment must have EPM info and EPM attribute.");
                switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorName:
                case SyndicationItemProperty.ContributorName:
                    if (textPropertyValue != null)
                    {
                        if (personMetadata == null)
                        {
                            personMetadata = new AtomPersonMetadata();
                        }

                        personMetadata.Name = textPropertyValue;
                    }

                    break;

                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.ContributorEmail:
                    if (textPropertyValue != null && textPropertyValue.Length > 0)
                    {
                        if (personMetadata == null)
                        {
                            personMetadata = new AtomPersonMetadata();
                        }

                        personMetadata.Email = textPropertyValue;
                    }

                    break;

                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorUri:
                    if (textPropertyValue != null && textPropertyValue.Length > 0)
                    {
                        if (personMetadata == null)
                        {
                            personMetadata = new AtomPersonMetadata();
                        }

                        personMetadata.UriFromEpm = textPropertyValue;
                    }

                    break;

                default:
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WritePersonEpm));
                }
            }

            return(personMetadata);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Writes the syndication part of EPM for an entry into ATOM metadata OM.
        /// </summary>
        /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param>
        /// <param name="entityType">The type of the entry.</param>
        /// <returns>The ATOM metadata OM with the EPM values populated.</returns>
        private AtomEntryMetadata WriteEntryEpm(
            EntryPropertiesValueCache epmValueCache,
            IEdmEntityTypeReference entityType)
        {
            // If there are no syndication mappings, just return null.
            EpmTargetPathSegment syndicationRootSegment = this.epmTargetTree.SyndicationRoot;

            Debug.Assert(syndicationRootSegment != null, "EPM Target tree must always have syndication root.");
            if (syndicationRootSegment.SubSegments.Count == 0)
            {
                return(null);
            }

            foreach (EpmTargetPathSegment targetSegment in syndicationRootSegment.SubSegments)
            {
                if (targetSegment.HasContent)
                {
                    EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;
                    Debug.Assert(
                        epmInfo != null && epmInfo.Attribute != null,
                        "If the segment has content it must have EpmInfo which in turn must have the EPM attribute");

                    object propertyValue = this.ReadEntryPropertyValue(
                        epmInfo,
                        epmValueCache,
                        entityType);
                    string textPropertyValue = EpmWriterUtils.GetPropertyValueAsText(propertyValue);

                    switch (epmInfo.Attribute.TargetSyndicationItem)
                    {
                    case SyndicationItemProperty.Updated:
                        if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                        {
                            this.entryMetadata.UpdatedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior);
                        }
                        else
                        {
                            this.entryMetadata.Updated = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Updated, this.WriterBehavior);
                        }

                        break;

                    case SyndicationItemProperty.Published:
                        if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                        {
                            this.entryMetadata.PublishedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior);
                        }
                        else
                        {
                            this.entryMetadata.Published = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Published, this.WriterBehavior);
                        }

                        break;

                    case SyndicationItemProperty.Rights:
                        this.entryMetadata.Rights = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    case SyndicationItemProperty.Summary:
                        this.entryMetadata.Summary = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    case SyndicationItemProperty.Title:
                        this.entryMetadata.Title = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteEntryEpm_ContentTarget));
                    }
                }
                else
                {
                    this.WriteParentSegment(targetSegment, epmValueCache, entityType);
                }
            }

            return(this.entryMetadata);
        }