internal ODataAtomReader(ODataAtomInputContext atomInputContext, IEdmEntityType expectedEntityType, bool readingFeed) : base(atomInputContext, expectedEntityType, readingFeed, null) { this.atomInputContext = atomInputContext; this.atomEntryAndFeedDeserializer = new ODataAtomEntryAndFeedDeserializer(atomInputContext); if (this.atomInputContext.MessageReaderSettings.ReaderBehavior.EntryXmlCustomizationCallback != null) { this.atomInputContext.InitializeReaderCustomization(); this.atomEntryAndFeedDeserializersStack = new Stack<ODataAtomEntryAndFeedDeserializer>(); this.atomEntryAndFeedDeserializersStack.Push(this.atomEntryAndFeedDeserializer); } }
/// <summary> /// Constructor. /// </summary> /// <param name="atomInputContext">The input to read the payload from.</param> /// <param name="expectedEntityType">The expected entity type for the entry to be read (in case of entry reader) or entries in the feed to be read (in case of feed reader).</param> /// <param name="readingFeed">true if the reader is created for reading a feed; false when it is created for reading an entry.</param> internal ODataAtomReader(ODataAtomInputContext atomInputContext, IEdmEntityType expectedEntityType, bool readingFeed) : base(atomInputContext, expectedEntityType, readingFeed, null /*listener*/) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(atomInputContext != null, "atomInputContext != null"); this.atomInputContext = atomInputContext; this.atomEntryAndFeedDeserializer = new ODataAtomEntryAndFeedDeserializer(atomInputContext); if (this.atomInputContext.MessageReaderSettings.AtomEntryXmlCustomizationCallback != null) { this.atomInputContext.InitializeReaderCustomization(); this.atomEntryAndFeedDeserializersStack = new Stack<ODataAtomEntryAndFeedDeserializer>(); this.atomEntryAndFeedDeserializersStack.Push(this.atomEntryAndFeedDeserializer); } }
protected override bool ReadAtEntryEndImplementation() { bool isTopLevel = base.IsTopLevel; bool isExpandedLinkContent = base.IsExpandedLinkContent; bool emptyInline = base.CurrentEntry == null; base.PopScope(ODataReaderState.EntryEnd); if (!emptyInline) { bool flag4 = false; if (this.atomInputContext.MessageReaderSettings.ReaderBehavior.EntryXmlCustomizationCallback != null) { XmlReader objB = this.atomInputContext.PopCustomReader(); if (!object.ReferenceEquals(this.atomInputContext.XmlReader, objB)) { flag4 = true; this.atomEntryAndFeedDeserializersStack.Pop(); this.atomEntryAndFeedDeserializer = this.atomEntryAndFeedDeserializersStack.Peek(); } } if (!flag4) { this.atomEntryAndFeedDeserializer.ReadEntryEnd(); } } bool flag5 = true; if (isTopLevel) { this.atomEntryAndFeedDeserializer.ReadPayloadEnd(); this.ReplaceScope(ODataReaderState.Completed); return false; } if (isExpandedLinkContent) { this.atomEntryAndFeedDeserializer.ReadNavigationLinkContentAfterExpansion(emptyInline); this.ReplaceScope(ODataReaderState.NavigationLinkEnd); return flag5; } if (this.atomEntryAndFeedDeserializer.ReadFeedContent(this.CurrentFeedState, base.IsExpandedLinkContent)) { this.ReadEntryStart(); return flag5; } this.ReplaceScope(ODataReaderState.FeedEnd); return flag5; }
private void ReadEntryStart() { ODataEntry entry = ReaderUtils.CreateNewEntry(); if (this.atomInputContext.MessageReaderSettings.ReaderBehavior.EntryXmlCustomizationCallback != null) { this.atomEntryAndFeedDeserializer.VerifyEntryStart(); Uri xmlBaseUri = this.atomInputContext.XmlReader.XmlBaseUri; XmlReader objB = this.atomInputContext.MessageReaderSettings.ReaderBehavior.EntryXmlCustomizationCallback(entry, this.atomInputContext.XmlReader, this.atomInputContext.XmlReader.ParentXmlBaseUri); if (objB != null) { if (object.ReferenceEquals(this.atomInputContext.XmlReader, objB)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataAtomReader_EntryXmlCustomizationCallbackReturnedSameInstance); } this.atomInputContext.PushCustomReader(objB, xmlBaseUri); this.atomEntryAndFeedDeserializer = new ODataAtomEntryAndFeedDeserializer(this.atomInputContext); this.atomEntryAndFeedDeserializersStack.Push(this.atomEntryAndFeedDeserializer); } else { this.atomInputContext.PushCustomReader(this.atomInputContext.XmlReader, null); } } this.atomEntryAndFeedDeserializer.ReadEntryStart(entry); this.EnterScope(ODataReaderState.EntryStart, entry, base.CurrentEntityType); AtomScope currentScope = (AtomScope) base.CurrentScope; currentScope.DuplicatePropertyNamesChecker = this.atomInputContext.CreateDuplicatePropertyNamesChecker(); string entityTypeNameFromPayload = this.atomEntryAndFeedDeserializer.FindTypeName(); base.ApplyEntityTypeNameFromPayload(entityTypeNameFromPayload); if (base.CurrentFeedValidator != null) { base.CurrentFeedValidator.ValidateEntry(base.CurrentEntityType); } ODataEntityPropertyMappingCache cache = this.atomInputContext.Model.EnsureEpmCache(this.CurrentEntryState.EntityType, 0x7fffffff); if (cache != null) { currentScope.CachedEpm = cache; } if (this.atomEntryAndFeedDeserializer.XmlReader.IsEmptyElement) { this.CurrentEntryState.EntryElementEmpty = true; } else { this.atomEntryAndFeedDeserializer.XmlReader.Read(); if (this.atomInputContext.UseServerApiBehavior) { this.CurrentEntryState.FirstNavigationLinkDescriptor = null; } else { this.CurrentEntryState.FirstNavigationLinkDescriptor = this.atomEntryAndFeedDeserializer.ReadEntryContent(this.CurrentEntryState); } } }
/// <summary> /// Reads the start of an entry and sets up the reader state correctly. /// </summary> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The method will fail if it's not atom:entry. /// Post-Condition: XmlNodeType.Element (empty) atom:entry - The entry element when reading entry and the entry element is empty. /// XmlNodeType.EndElement atom:entry - The end element of the entry (if there were no nav. links. in it) /// XmlNodeType.Element atom:link - The start tag of the atom:link which represents the first navigation link in the entry. /// </remarks> private void ReadEntryStart() { this.atomEntryAndFeedDeserializer.AssertXmlCondition(XmlNodeType.Element); ODataEntry entry = ReaderUtils.CreateNewEntry(); if (this.atomInputContext.MessageReaderSettings.AtomEntryXmlCustomizationCallback != null) { this.atomEntryAndFeedDeserializer.VerifyEntryStart(); // Note that we're passing in the buffering reader positioned on the atom:entry element, but that element might have been already buffered // although the reader is currently not in buffering mode. // We must remember the base URI, since we use it as the starting xml:base for the wrapped buffering reader over the reader returned // from the customization callback; Uri entryXmlBaseUri = this.atomInputContext.XmlReader.XmlBaseUri; this.atomInputContext.XmlReader.AssertNotBuffering(); XmlReader entryReader = this.atomInputContext.MessageReaderSettings.AtomEntryXmlCustomizationCallback( entry, this.atomInputContext.XmlReader, this.atomInputContext.XmlReader.ParentXmlBaseUri); if (entryReader != null) { if (object.ReferenceEquals(this.atomInputContext.XmlReader, entryReader)) { throw new ODataException(o.Strings.ODataAtomReader_EntryXmlCustomizationCallbackReturnedSameInstance); } // Push the new reader. // We need to use the xml:base from the entry element itself, since the inner reader starts on that element // and thus our wrapping reader will not see that element again and will not read it's xml:base value for the stack. this.atomInputContext.PushCustomReader(entryReader, entryXmlBaseUri); // And push a new deserializer. this.atomEntryAndFeedDeserializer = new ODataAtomEntryAndFeedDeserializer(this.atomInputContext); this.atomEntryAndFeedDeserializersStack.Push(this.atomEntryAndFeedDeserializer); } else { // Push the same reader so that we know that customization didn't happen at this level. this.atomInputContext.PushCustomReader(this.atomInputContext.XmlReader, /*xmlBaseUri*/ null); } } // Read the start of the entry (etag and type name) this.atomEntryAndFeedDeserializer.ReadEntryStart(entry); // Setup the new entry state this.EnterScope(ODataReaderState.EntryStart, entry, this.CurrentEntityType); AtomScope entryScope = (AtomScope)this.CurrentScope; entryScope.DuplicatePropertyNamesChecker = this.atomInputContext.CreateDuplicatePropertyNamesChecker(); // Read ahead to detect the type name and use it. string typeNameFromPayload = this.atomEntryAndFeedDeserializer.FindTypeName(); this.ApplyEntityTypeNameFromPayload(typeNameFromPayload); // Validate type with feed validator if available if (this.CurrentFeedValidator != null) { this.CurrentFeedValidator.ValidateEntry(this.CurrentEntityType); } // NOTE: when reading, we assume the model has been validated already and thus pass int.MaxValue for the maxMappingCount. ODataEntityPropertyMappingCache cachedEpm = this.atomInputContext.Model.EnsureEpmCache(this.CurrentEntryState.EntityType, /*maxMappingCount*/ int.MaxValue); if (cachedEpm != null) { entryScope.CachedEpm = cachedEpm; } Debug.Assert( this.atomEntryAndFeedDeserializer.XmlReader.NodeType == XmlNodeType.Element && this.atomEntryAndFeedDeserializer.XmlReader.LocalName == AtomConstants.AtomEntryElementName && this.atomEntryAndFeedDeserializer.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The XML reader must be on the atom:entry element by now."); if (this.atomEntryAndFeedDeserializer.XmlReader.IsEmptyElement) { // If the entry element is empty, remember that so that we can easily decide what to do next time the reader is called. this.CurrentEntryState.EntryElementEmpty = true; } else { // Move to the first child node of the entry this.atomEntryAndFeedDeserializer.XmlReader.Read(); if (this.atomInputContext.UseServerApiBehavior) { this.CurrentEntryState.FirstNavigationLinkDescriptor = null; } else { // Read the entry content. // If we find a nav. link, store it on the scope for reporting once we report the entry itself. this.CurrentEntryState.FirstNavigationLinkDescriptor = this.atomEntryAndFeedDeserializer.ReadEntryContent(this.CurrentEntryState); } } }
/// <summary> /// Implementation of the reader logic when in state 'EntryEnd'. /// </summary> /// <returns>true if more items can be read from the reader; otherwise false.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (empty) atom:entry - The empty start tag of atom:entry. /// XmlNodeType.EndElement atom:entry - The end element of the atom:entry. /// XmlNodeType.Element (empty) m:inline - the empty m:inline element of an expanded null entry. /// XmlNodeType.EndElement m:inline - the end element m:inline of an expanded null entry. /// Post-Condition: Any - The node right after the top-level atom:entry element. /// XmlNodeType.EndElement atom:feed - The end element of the parent feed for the entry. /// XmlNodeType.Element (empty) atom:entry - The empty start tag of the next entry in the parent feed. /// XmlNodeType.EndElement atom:entry - The end element of the next entry in the parent feed (if it had no nav. links). /// XmlNodeType.Element atom:link - The start tag of the atom:link which represents the first navigation link in the next entry in the parent feed. /// XmlNodeType.EndElement atom:link - The end of parent expanded link. /// </remarks> protected override bool ReadAtEntryEndImplementation() { bool isTopLevel = this.IsTopLevel; bool isExpandedLinkContent = this.IsExpandedLinkContent; bool isNullEntry = this.CurrentEntry == null; this.PopScope(ODataReaderState.EntryEnd); Debug.Assert(!isNullEntry || isExpandedLinkContent, "Null entry can only occure inside an expanded link."); if (!isNullEntry) { bool entryXmlCustomizationUsed = false; if (this.atomInputContext.MessageReaderSettings.AtomEntryXmlCustomizationCallback != null) { XmlReader entryReader = this.atomInputContext.PopCustomReader(); if (!object.ReferenceEquals(this.atomInputContext.XmlReader, entryReader)) { entryXmlCustomizationUsed = true; // Pop the deserializer so that we use the one for the parent reader from now on. this.atomEntryAndFeedDeserializersStack.Pop(); this.atomEntryAndFeedDeserializer = this.atomEntryAndFeedDeserializersStack.Peek(); } } // We assume that if the customization was used, then the parent reader was moved to after the end-element (empty start-element) // only if no customization was used we need to read over it here (note that we leave it on the end-element when we report the EndEntry // so that streaming scenarios work as expected, that is reading one entry reads just that from the stream and not a byte more). if (!entryXmlCustomizationUsed) { // Read over the end element (or the empty start element) this.atomEntryAndFeedDeserializer.ReadEntryEnd(); } } bool result = true; if (isTopLevel) { Debug.Assert(this.State == ODataReaderState.Start, "this.State == ODataReaderState.Start"); // Read the end of the payload this.atomEntryAndFeedDeserializer.ReadPayloadEnd(); // replace the 'Start' scope with the 'Completed' scope this.ReplaceScope(ODataReaderState.Completed); result = false; } else if (isExpandedLinkContent) { #if DEBUG if (isNullEntry) { this.atomEntryAndFeedDeserializer.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert( this.atomEntryAndFeedDeserializer.XmlReader.LocalName == AtomConstants.ODataInlineElementName && this.atomEntryAndFeedDeserializer.XmlReader.NamespaceURI == AtomConstants.ODataMetadataNamespace, "The reader must be positied on the m:inline empty start tag, or end element"); } #endif this.atomEntryAndFeedDeserializer.ReadNavigationLinkContentAfterExpansion(isNullEntry); // replace the 'NavigationLinkStart' scope with the 'NavigationLinkEnd' scope Debug.Assert(this.CurrentScope.State == ODataReaderState.NavigationLinkStart, "Should be in NavigationLinkStart state."); this.ReplaceScope(ODataReaderState.NavigationLinkEnd); } else { // End of entry in a feed Debug.Assert(this.State == ODataReaderState.FeedStart, "Expected reader to be in state feed start before reading the next entry."); // Continue reading the content of the parent feed if (this.atomEntryAndFeedDeserializer.ReadFeedContent(this.CurrentFeedState, this.IsExpandedLinkContent)) { this.atomEntryAndFeedDeserializer.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.atomEntryAndFeedDeserializer.XmlReader.LocalName == AtomConstants.AtomEntryElementName && this.atomEntryAndFeedDeserializer.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The reader must be on the start element of atom:entry."); // Found another entry in the feed this.ReadEntryStart(); } else { this.atomEntryAndFeedDeserializer.AssertXmlCondition(XmlNodeType.EndElement); Debug.Assert( this.atomEntryAndFeedDeserializer.XmlReader.LocalName == AtomConstants.AtomFeedElementName && this.atomEntryAndFeedDeserializer.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The reader must be on the end element of atom:feed."); // End of the parent feed this.ReplaceScope(ODataReaderState.FeedEnd); } } return result; }