/// <summary>
        /// Reads any next link annotation immediately after the end of a feed.
        /// </summary>
        /// <param name="feed">The feed being read.</param>
        /// <param name="expandedNavigationLinkInfo">The information about the expanded link. This must be non-null if we're reading an expanded feed, and must be null if we're reading a top-level feed.</param>
        /// <param name="duplicatePropertyNamesChecker">The top-level duplicate property names checker, if we're reading a top-level feed.</param>
        internal void ReadNextLinkAnnotationAtFeedEnd(
            ODataFeedBase feed,
            ODataJsonLightReaderNavigationLinkInfo expandedNavigationLinkInfo,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            Debug.Assert(feed != null, "feed != null");

            // Check for annotations on the feed that occur after the feed itself. (Note: the only allowed one is odata.nextLink, and we fail for anything else.)
            // We do this slightly differently depending on whether the feed was an expanded navigation or a top-level feed.
            if (expandedNavigationLinkInfo != null)
            {
                this.ReadExpandedFeedAnnotationsAtFeedEnd(feed, expandedNavigationLinkInfo);
            }
            else
            {
                Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");

                // Check for feed instance annotations that appear after the feed.
                bool isReordering = this.JsonReader is ReorderingJsonReader;
                this.ReadTopLevelFeedAnnotations(feed, duplicatePropertyNamesChecker, /*forFeedStart*/false, /*readAllFeedProperties*/isReordering);
            }
        }
        /// <summary>
        /// Reads the value of a feed annotation (count or next link).
        /// </summary>
        /// <param name="annotationName">The name of the annotation found.</param>
        /// <param name="feed">The feed to read the annotation for; if non-null, the annotation value will be assigned to the feed.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker instance.</param>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.PrimitiveValue         The value of the annotation
        /// Post-Condition: JsonNodeType.EndObject              The end of the feed object
        ///                 JsonNodeType.Property               The next annotation after the current annotation
        /// </remarks>
        internal void ReadAndApplyFeedInstanceAnnotationValue(string annotationName, ODataFeedBase feed, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            Debug.Assert(!string.IsNullOrEmpty(annotationName), "!string.IsNullOrEmpty(annotationName)");
            Debug.Assert(feed != null, "feed != null");

            switch (annotationName)
            {
                case ODataAnnotationNames.ODataCount:
                    feed.Count = this.ReadAndValidateAnnotationAsLongForIeee754Compatible(ODataAnnotationNames.ODataCount);
                    break;

                case ODataAnnotationNames.ODataNextLink:
                    feed.NextPageLink = this.ReadAndValidateAnnotationStringValueAsUri(ODataAnnotationNames.ODataNextLink);
                    break;

                case ODataAnnotationNames.ODataDeltaLink:
                    feed.DeltaLink = this.ReadAndValidateAnnotationStringValueAsUri(ODataAnnotationNames.ODataDeltaLink);
                    break;

                default:
                    ODataAnnotationNames.ValidateIsCustomAnnotationName(annotationName);
                    Debug.Assert(
                        !this.MessageReaderSettings.ShouldSkipAnnotation(annotationName),
                        "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(annotationName) -- otherwise we should have already skipped the custom annotation and won't see it here.");
                    object instanceAnnotationValue = this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, annotationName);
                    feed.InstanceAnnotations.Add(new ODataInstanceAnnotation(annotationName, instanceAnnotationValue.ToODataValue()));
                    break;
            }
        }
        /// <summary>
        /// Checks if there is a next link annotation immediately after an expanded feed, and reads and stores it if there is one. 
        /// We fail here if we encounter any other property annotation for the expanded navigation (since these should come before the property itself).
        /// </summary>
        /// <param name="feed">The feed that was just read.</param>
        /// <param name="expandedNavigationLinkInfo">The information for the current expanded navigation link being read.</param>
        private void ReadExpandedFeedAnnotationsAtFeedEnd(ODataFeedBase feed, ODataJsonLightReaderNavigationLinkInfo expandedNavigationLinkInfo)
        {
            Debug.Assert(expandedNavigationLinkInfo != null, "expandedNavigationLinkInfo != null");
            Debug.Assert(expandedNavigationLinkInfo.NavigationLink.IsCollection == true, "Only collection navigation properties can have feed content.");

            // Look at the next property in the owning entry, if it's a property annotation for the expanded navigation link info property, read it.
            string propertyName, annotationName;
            while (this.JsonReader.NodeType == JsonNodeType.Property &&
                   TryParsePropertyAnnotation(this.JsonReader.GetPropertyName(), out propertyName, out annotationName) &&
                   string.CompareOrdinal(propertyName, expandedNavigationLinkInfo.NavigationLink.Name) == 0)
            {
                if (!this.ReadingResponse)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedPropertyAnnotation(propertyName, annotationName));
                }

                // Read over the property name.
                this.JsonReader.Read();

                switch (annotationName)
                {
                    case ODataAnnotationNames.ODataNextLink:
                        if (feed.NextPageLink != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_DuplicateExpandedFeedAnnotation(ODataAnnotationNames.ODataNextLink, expandedNavigationLinkInfo.NavigationLink.Name));
                        }

                        // Read the property value.
                        feed.NextPageLink = this.ReadAndValidateAnnotationStringValueAsUri(ODataAnnotationNames.ODataNextLink);
                        break;

                    case ODataAnnotationNames.ODataCount:
                        if (feed.Count != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_DuplicateExpandedFeedAnnotation(ODataAnnotationNames.ODataCount, expandedNavigationLinkInfo.NavigationLink.Name));
                        }

                        // Read the property value.
                        feed.Count = this.ReadAndValidateAnnotationAsLongForIeee754Compatible(ODataAnnotationNames.ODataCount);
                        break;

                    case ODataAnnotationNames.ODataDeltaLink:   // Delta links are not supported on expanded feeds.
                    default:
                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedPropertyAnnotationAfterExpandedFeed(annotationName, expandedNavigationLinkInfo.NavigationLink.Name));
                }
            }
        }
        /// <summary>
        /// Reads the feed instance annotations for a top-level feed.
        /// </summary>
        /// <param name="feed">The <see cref="ODataFeed"/> to read the instance annotations for.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the top-level scope.</param>
        /// <param name="forFeedStart">true when parsing the instance annotations before the feed property; 
        /// false when parsing the instance annotations after the feed property.</param>
        /// <param name="readAllFeedProperties">true if we should scan ahead for the annotations and ignore the actual data properties (used with
        /// the reordering reader); otherwise false.</param>
        internal void ReadTopLevelFeedAnnotations(ODataFeedBase feed, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forFeedStart, bool readAllFeedProperties)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");
            this.JsonReader.AssertNotBuffering();

            bool buffering = false;
            try
            {
                while (this.JsonReader.NodeType == JsonNodeType.Property)
                {
                    bool foundValueProperty = false;

                    if (!forFeedStart && readAllFeedProperties)
                    {
                        // If this is not called for reading FeedStart and we already scanned ahead and processed all feed properties, we already checked for duplicate property names.
                        // Use an empty duplicate property name checker since this.ParseProperty() read through the same property annotation of instance annotations again. 
                        duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(/*allowDuplicateProperties*/ true, this.JsonLightInputContext.ReadingResponse, !this.JsonLightInputContext.MessageReaderSettings.EnableFullValidation);
                    }

                    this.ProcessProperty(
                        duplicatePropertyNamesChecker,
                        this.ReadTypePropertyAnnotationValue,
                        (propertyParseResult, propertyName) =>
                        {
                            switch (propertyParseResult)
                            {
                                case PropertyParsingResult.ODataInstanceAnnotation:
                                case PropertyParsingResult.CustomInstanceAnnotation:
                                    // When we are reading the start of a feed (in scan-ahead mode or not) or when
                                    // we read the end of a feed and not in scan-ahead mode, read the value;
                                    // otherwise skip it.
                                    if (forFeedStart || !readAllFeedProperties)
                                    {
                                        this.ReadAndApplyFeedInstanceAnnotationValue(propertyName, feed, duplicatePropertyNamesChecker);
                                    }
                                    else
                                    {
                                        this.JsonReader.SkipValue();
                                    }

                                    break;

                                case PropertyParsingResult.PropertyWithValue:
                                    if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) == 0)
                                    {
                                        // We found the feed property and are done parsing property annotations;
                                        // When we are in the mode where we scan ahead and read all feed properties
                                        // (for the reordering scenario), we have to start buffering and continue 
                                        // reading. Otherwise we found the feed's data property and are done.
                                        if (readAllFeedProperties)
                                        {
                                            this.JsonReader.StartBuffering();
                                            buffering = true;

                                            this.JsonReader.SkipValue();
                                        }
                                        else
                                        {
                                            foundValueProperty = true;
                                        }
                                    }
                                    else
                                    {
                                        throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_InvalidPropertyInTopLevelFeed(propertyName, JsonLightConstants.ODataValuePropertyName));
                                    }

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

                                case PropertyParsingResult.EndOfObject:
                                    break;

                                case PropertyParsingResult.MetadataReferenceProperty:
                                    if (!(feed is ODataFeed))
                                    {
                                        throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));
                                    }

                                    this.ReadMetadataReferencePropertyValue((ODataFeed)feed, propertyName);
                                    break;

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

                    if (foundValueProperty)
                    {
                        return;
                    }
                }
            }
            finally
            {
                if (buffering)
                {
                    Debug.Assert(readAllFeedProperties, "Expect the reader to be in buffering mode only when scanning to the end.");
                    this.JsonReader.StopBuffering();
                }
            }

            if (forFeedStart && !readAllFeedProperties)
            {
                // We did not find any properties or only instance annotations.
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_ExpectedFeedPropertyNotFound(JsonLightConstants.ODataValuePropertyName));
            }
        }
            /// <summary>
            /// Create an ODataFeed cloning from an ODataFeedBase.
            /// </summary>
            /// <param name="feedBase">The feed to be cloned.</param>
            /// <returns>The created feed.</returns>
            private static ODataFeed Clone(ODataFeedBase feedBase)
            {
                Debug.Assert(feedBase != null, "feedBase != null");

                return new ODataFeed
                {
                    Count = feedBase.Count,
                    DeltaLink = feedBase.DeltaLink,
                    Id = feedBase.Id,
                    InstanceAnnotations = feedBase.InstanceAnnotations,
                    NextPageLink = feedBase.NextPageLink
                };
            }