Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / AtomParser.cs / 1305376 / AtomParser.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Parses an XML stream into structured AtomEntry or related instances.
//
//---------------------------------------------------------------------
namespace System.Data.Services.Client
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Text;
#endregion Namespaces.
/// Parser for DataService payloads (mostly ATOM).
///
/// There are four types of documents parsed:
/// 1. A single non-entity value.
/// 2. A list of non-entity values.
/// 3. An entity.
/// 4. A feed.
///
/// In case of (1), the parser will go through these states:
/// None -> Custom -> Finished
///
/// In case of (2), the parser will go through these states:
/// None -> Custom -> Custom -> Finished
///
/// In case of (3), the parser will go through these states:
/// None -> Entry -> Finished
///
/// In case of (4), the parser will go through these states:
/// None -> (FeedData | Entry) -> (FeedData | Entry) -> Finished
///
/// Note that the parser will always stop on 'top-level' packets; all recursive data
/// structures are handled internally.
///
[DebuggerDisplay("AtomParser {kind} {reader}")]
internal class AtomParser
{
#region Private fields.
/// Callback invoked each time an ATOM entry is found.
///
/// This callback takes the current XmlReader and returns a
/// subtree XmlReader and an object that is assigned to the
/// entry's Tag property.
///
private readonly Func> entryCallback;
/// Stack of available XmlReaders.
private readonly Stack readers;
/// Scheme used to find type information on ATOM category elements.
private readonly string typeScheme;
/// ATOM entry being parsed.
private AtomEntry entry;
/// ATOM feed being parsed.
private AtomFeed feed;
/// Current data kind (nothing, entry, feed, custom-top-level-thingy, etc).
private AtomDataKind kind;
/// Current .
private XmlReader reader;
/// The data namespace
private string currentDataNamespace;
#endregion Private fields.
#region Constructors.
/// Initializes a new instance.
/// to parse content from.
///
/// Callback invoked each time an ATOM entry is found; see the comments
/// on the entryCallback field.
///
///
/// Scheme used to find type information on ATOM category elements.
///
/// The xml document's DataWeb Namespace
internal AtomParser(XmlReader reader, Func> entryCallback, string typeScheme, string currentDataNamespace)
{
Debug.Assert(reader != null, "reader != null");
Debug.Assert(typeScheme != null, "typeScheme != null");
Debug.Assert(entryCallback != null, "entryCallback != null");
Debug.Assert(!String.IsNullOrEmpty(currentDataNamespace), "currentDataNamespace is empty or null");
this.reader = reader;
this.readers = new Stack();
this.entryCallback = entryCallback;
this.typeScheme = typeScheme;
this.currentDataNamespace = currentDataNamespace;
Debug.Assert(this.kind == AtomDataKind.None, "this.kind == AtomDataKind.None -- otherwise not initialized correctly");
}
#endregion Constructors.
#region Internal properties.
/// Entry being materialized; possibly null.
internal AtomEntry CurrentEntry
{
get
{
return this.entry;
}
}
/// Feed being materialized; possibly null.
internal AtomFeed CurrentFeed
{
get
{
return this.feed;
}
}
/// Kind of ATOM data available on the parser.
internal AtomDataKind DataKind
{
get
{
return this.kind;
}
}
///
/// Returns true if the current element is in the data web namespace
///
internal bool IsDataWebElement
{
get { return this.reader.NamespaceURI == this.currentDataNamespace; }
}
#endregion Internal properties.
#region Internal methods.
///
/// Creates an instance for ATOM entries.
///
/// Reader being used.
///
/// A pair of an XmlReader instance and an object to be assigned
/// to the Tag on the entry (available for materialization callbacks
/// later in the pipeline).
///
///
/// A no-op implementation would do this instead:
///
/// return new KeyValuePair<XmlReader, object>(reader.ReadSubtree(), null);
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "not required")]
internal static KeyValuePair XElementBuilderCallback(XmlReader reader)
{
Debug.Assert(reader != null, "reader != null");
Debug.Assert(reader is Xml.XmlWrappingReader, "reader must be a instance of XmlWrappingReader");
string readerBaseUri = reader.BaseURI;
XElement element = XElement.Load(reader.ReadSubtree(), LoadOptions.None);
return new KeyValuePair(Xml.XmlWrappingReader.CreateReader(readerBaseUri, element.CreateReader()), element);
}
#endregion Internal methods.
#region Internal methods.
/// Consumes the next chunk of content from the underlying XML reader.
///
/// true if another piece of content is available, identified by DataKind.
/// false if there is no more content.
///
internal bool Read()
{
// When an external caller 'insists', we'll come all the way down (which is the 'most local'
// scope at which this is known), and unwind as a no-op.
if (this.DataKind == AtomDataKind.Finished)
{
return false;
}
while (this.reader.Read())
{
if (ShouldIgnoreNode(this.reader))
{
continue;
}
Debug.Assert(
this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement,
"this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement -- otherwise we should have ignored or thrown");
AtomDataKind readerData = ParseStateForReader(this.reader);
if (this.reader.NodeType == XmlNodeType.EndElement)
{
// The only case in which we expect to see an end-element at the top level
// is for a feed. Custom elements and entries should be consumed by
// their own parsing methods. However we are tolerant of additional EndElements,
// which at this point mean we have nothing else to consume.
break;
}
switch (readerData)
{
case AtomDataKind.Custom:
if (this.DataKind == AtomDataKind.None)
{
this.kind = AtomDataKind.Custom;
return true;
}
else
{
MaterializeAtom.SkipToEnd(this.reader);
continue;
}
case AtomDataKind.Entry:
this.kind = AtomDataKind.Entry;
this.ParseCurrentEntry(out this.entry);
return true;
case AtomDataKind.Feed:
if (this.DataKind == AtomDataKind.None)
{
this.feed = new AtomFeed();
this.kind = AtomDataKind.Feed;
return true;
}
throw new InvalidOperationException(Strings.AtomParser_FeedUnexpected);
case AtomDataKind.FeedCount:
this.ParseCurrentFeedCount();
break;
case AtomDataKind.PagingLinks:
if (this.feed == null)
{
// paging link outside of feed?
throw new InvalidOperationException(Strings.AtomParser_PagingLinkOutsideOfFeed);
}
this.kind = AtomDataKind.PagingLinks;
this.ParseCurrentFeedPagingLinks();
return true;
default:
Debug.Assert(false, "Atom Parser is in a wrong state...Did you add a new AtomDataKind?");
break;
}
}
this.kind = AtomDataKind.Finished;
this.entry = null;
return false;
}
/// Reads the current property value from the reader.
/// A structured property instance.
///
/// This method should only be called for top-level complex properties.
///
/// For top-level primitive values,
/// should be used to preserve V1 behavior in which mixed-content
/// XML elements are allowed.
///
internal AtomContentProperty ReadCurrentPropertyValue()
{
Debug.Assert(
this.kind == AtomDataKind.Custom,
"this.kind == AtomDataKind.Custom -- otherwise caller shouldn't invoke ReadCurrentPropertyValue");
return this.ReadPropertyValue();
}
/// Reads the current property value from the reader as a string.
/// A structured property instance.
///
/// This method should only be called for top-level primitive types.
///
/// For top-level complex type values,
/// should be used to capture all information.
///
internal string ReadCustomElementString()
{
Debug.Assert(
this.kind == AtomDataKind.Custom,
"this.kind == AtomDataKind.Custom -- otherwise caller shouldn't invoke ReadCustomElementString");
return MaterializeAtom.ReadElementString(this.reader, true);
}
/// Replaces the current reader with the specified reader.
/// New reader to use.
///
/// This is a very odd method, here only to allow inline count to
/// read everything ahead and "reopen" where we are. No checks are
/// done as to whether we are in a safe place to do so.
///
internal void ReplaceReader(XmlReader newReader)
{
Debug.Assert(newReader != null, "newReader != null");
this.reader = newReader;
}
#endregion Internal methods.
#region Private methods.
///
/// Determines what the parse state should be for the specified
/// .
///
/// Reader to check.
/// The data kind derived from the current element.
///
/// Note that no previous state is considered, so state transitions
/// aren't handled by the method - instead, certain known elements
/// are mapped to parser states.
///
private static AtomDataKind ParseStateForReader(XmlReader reader)
{
Debug.Assert(reader != null, "reader != null");
Debug.Assert(
reader.NodeType == XmlNodeType.Element || reader.NodeType == XmlNodeType.EndElement,
"reader.NodeType == XmlNodeType.Element || EndElement -- otherwise can't determine");
AtomDataKind result = AtomDataKind.Custom;
string elementName = reader.LocalName;
string namespaceURI = reader.NamespaceURI;
if (Util.AreSame(XmlConstants.AtomNamespace, namespaceURI))
{
if (Util.AreSame(XmlConstants.AtomEntryElementName, elementName))
{
result = AtomDataKind.Entry;
}
else if (Util.AreSame(XmlConstants.AtomFeedElementName, elementName))
{
result = AtomDataKind.Feed;
}
else if (Util.AreSame(XmlConstants.AtomLinkElementName, elementName) &&
Util.AreSame(XmlConstants.AtomLinkNextAttributeString, reader.GetAttribute(XmlConstants.AtomLinkRelationAttributeName)))
{
result = AtomDataKind.PagingLinks;
}
}
else if (Util.AreSame(XmlConstants.DataWebMetadataNamespace, namespaceURI))
{
if (Util.AreSame(XmlConstants.RowCountElement, elementName))
{
result = AtomDataKind.FeedCount;
}
}
return result;
}
///
/// Reads from the specified and moves to the
/// child element which should match the specified name.
///
/// Reader to consume.
/// Expected local name of child element.
/// Expected namespace of child element.
///
/// true if the is left position on a child
/// with the given name; false otherwise.
///
private static bool ReadChildElement(XmlReader reader, string localName, string namespaceUri)
{
Debug.Assert(localName != null, "localName != null");
Debug.Assert(namespaceUri != null, "namespaceUri != null");
Debug.Assert(!reader.IsEmptyElement, "!reader.IsEmptyElement");
Debug.Assert(reader.NodeType != XmlNodeType.EndElement, "reader.NodeType != XmlNodeType.EndElement");
return reader.Read() && reader.IsStartElement(localName, namespaceUri);
}
///
/// Skips all content on the specified until
/// the specified is reached; such that
/// a call to .Read() will move to the sibling of the element at
/// .
///
/// Reader to advance.
/// Desired depth.
///
/// The reader may already be on an element at ;
/// if it's an empty element (<foo />) then the reader isn't
/// moved.
///
private static void SkipToEndAtDepth(XmlReader reader, int depth)
{
Debug.Assert(reader != null, "reader != null");
Debug.Assert(reader.Depth >= depth, "reader.Depth >= depth");
while (!(reader.Depth == depth &&
(reader.NodeType == XmlNodeType.EndElement ||
(reader.NodeType == XmlNodeType.Element && reader.IsEmptyElement))))
{
reader.Read();
}
}
///
/// Reads the text inside the element on the .
///
/// Reader to get text from.
/// The text inside the specified .
///
/// This method was designed to be compatible with the results
/// of evaluating the text of an XElement.
///
/// In short, this means that nulls are never returned, and
/// that all non-text nodes are ignored (but elements are
/// recursed into).
///
private static string ReadElementStringForText(XmlReader reader)
{
Debug.Assert(reader != null, "reader != null");
if (reader.IsEmptyElement)
{
return String.Empty;
}
StringBuilder result = new StringBuilder();
int depth = reader.Depth;
while (reader.Read())
{
if (reader.Depth == depth)
{
Debug.Assert(
reader.NodeType == XmlNodeType.EndElement,
"reader.NodeType == XmlNodeType.EndElement -- otherwise XmlReader is acting odd");
break;
}
if (reader.NodeType == XmlNodeType.SignificantWhitespace ||
reader.NodeType == XmlNodeType.Text)
{
result.Append(reader.Value);
}
}
return result.ToString();
}
///
/// Checks whether the current node on the specified
/// should be ignored.
///
/// Reader to check.
/// true if the node should be ignored; false if it should be processed.
///
/// This method will throw an exception on unexpected content (CDATA, entity references,
/// text); therefore it should not be used if mixed content is allowed.
///
private static bool ShouldIgnoreNode(XmlReader reader)
{
Debug.Assert(reader != null, "reader != null");
switch (reader.NodeType)
{
case XmlNodeType.CDATA:
case XmlNodeType.EntityReference:
case XmlNodeType.EndEntity:
Error.ThrowInternalError(InternalError.UnexpectedXmlNodeTypeWhenReading);
break;
case XmlNodeType.Text:
case XmlNodeType.SignificantWhitespace:
// throw Error.InvalidOperation(Strings.Deserialize_MixedContent(currentType.ElementTypeName));
Error.ThrowInternalError(InternalError.UnexpectedXmlNodeTypeWhenReading);
break;
case XmlNodeType.Element:
case XmlNodeType.EndElement:
return false;
default:
break;
}
return true;
}
///
/// Checks if the given content type string matches with 'application/xml' or
/// 'application/atom+xml' case insensitively.
///
/// Input content type.
/// true if match found, false otherwise.
private static bool IsAllowedContentType(string contentType)
{
return (String.Equals(XmlConstants.MimeApplicationXml, contentType, StringComparison.OrdinalIgnoreCase) ||
String.Equals(XmlConstants.MimeApplicationAtom, contentType, StringComparison.OrdinalIgnoreCase));
}
///
/// Checks if the given link type matches 'application/atom+xml;type=feed' or
/// 'application/atom+xml;type=entry' case insensitively.
///
/// Input link type.
/// Output parameter indicating whether we are reading a feed or an entry inline.
/// true if match found, false otherwise.
private static bool IsAllowedLinkType(string linkType, out bool isFeed)
{
isFeed = String.Equals(XmlConstants.LinkMimeTypeFeed, linkType, StringComparison.OrdinalIgnoreCase);
return isFeed ? true : String.Equals(XmlConstants.LinkMimeTypeEntry, linkType, StringComparison.OrdinalIgnoreCase);
}
///
/// Parses the content on the reader into the specified .
///
/// Target to read values into.
private void ParseCurrentContent(AtomEntry targetEntry)
{
Debug.Assert(targetEntry != null, "targetEntry != null");
Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");
string propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomContentSrcAttributeName, XmlConstants.AtomNamespace);
if (propertyValue != null)
{
// This is a media link entry
// Note that in this case we don't actually use this URL (or the link/edit-media URL)
// for editing. We rely on the Astoria URL convention (/propname/$value or just /$value)
if (!this.reader.IsEmptyElement)
{
throw Error.InvalidOperation(Strings.Deserialize_ExpectedEmptyMediaLinkEntryContent);
}
targetEntry.MediaLinkEntry = true;
targetEntry.MediaContentUri = new Uri(propertyValue, UriKind.RelativeOrAbsolute);
}
else
{
// This is a regular (non-media link) entry
if (targetEntry.MediaLinkEntry.HasValue && targetEntry.MediaLinkEntry.Value)
{
// This means we saw a element but now we have a Content element
// that's not just a media link entry pointer (src)
throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
}
targetEntry.MediaLinkEntry = false;
propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.AtomNamespace);
if (AtomParser.IsAllowedContentType(propertyValue))
{
if (this.reader.IsEmptyElement)
{
return;
}
if (ReadChildElement(this.reader, XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace))
{
this.ReadCurrentProperties(targetEntry.DataValues);
}
else if (this.reader.NodeType != XmlNodeType.EndElement)
{
throw Error.InvalidOperation(Strings.Deserialize_NotApplicationXml);
}
}
}
}
/// Parses a link for the specified .
/// Entry to update with link information.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "not required to dispose of the nested XmlReader")]
private void ParseCurrentLink(AtomEntry targetEntry)
{
Debug.Assert(targetEntry != null, "targetEntry != null");
Debug.Assert(
this.reader.NodeType == XmlNodeType.Element,
"this.reader.NodeType == XmlNodeType.Element -- otherwise we shouldn't try to parse a link");
Debug.Assert(
this.reader.LocalName == "link",
"this.reader.LocalName == 'link' -- otherwise we shouldn't try to parse a link");
string relation = this.reader.GetAttribute(XmlConstants.AtomLinkRelationAttributeName);
if (relation == null)
{
return;
}
if (relation == XmlConstants.AtomEditRelationAttributeValue && targetEntry.EditLink == null)
{
// Only process the first link that has @rel='edit'.
string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
if (String.IsNullOrEmpty(href))
{
throw Error.InvalidOperation(Strings.Context_MissingEditLinkInResponseBody);
}
targetEntry.EditLink = this.ConvertHRefAttributeValueIntoURI(href);
}
else if (relation == XmlConstants.AtomSelfRelationAttributeValue && targetEntry.QueryLink == null)
{
// Only process the first link that has @rel='self'.
string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
if (String.IsNullOrEmpty(href))
{
throw Error.InvalidOperation(Strings.Context_MissingSelfLinkInResponseBody);
}
targetEntry.QueryLink = this.ConvertHRefAttributeValueIntoURI(href);
}
else if (relation == XmlConstants.AtomEditMediaRelationAttributeValue && targetEntry.MediaEditUri == null)
{
string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
if (String.IsNullOrEmpty(href))
{
throw Error.InvalidOperation(Strings.Context_MissingEditMediaLinkInResponseBody);
}
targetEntry.MediaEditUri = this.ConvertHRefAttributeValueIntoURI(href);
targetEntry.StreamETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);
}
if (!this.reader.IsEmptyElement)
{
string propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(relation);
if (propertyName == null)
{
return;
}
string propertyValueText = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName);
bool isFeed;
if (!IsAllowedLinkType(propertyValueText, out isFeed))
{
return;
}
if (!ReadChildElement(this.reader, XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace))
{
return;
}
bool emptyInlineCollection = this.reader.IsEmptyElement;
object propertyValue = null;
if (!emptyInlineCollection)
{
AtomFeed nestedFeed = null;
AtomEntry nestedEntry = null;
List feedEntries = null;
Debug.Assert(this.reader is Xml.XmlWrappingReader, "reader must be a instance of XmlWrappingReader");
string readerBaseUri = this.reader.BaseURI;
XmlReader nestedReader = Xml.XmlWrappingReader.CreateReader(readerBaseUri, this.reader.ReadSubtree());
nestedReader.Read();
Debug.Assert(nestedReader.LocalName == "inline", "nestedReader.LocalName == 'inline'");
AtomParser nested = new AtomParser(nestedReader, this.entryCallback, this.typeScheme, this.currentDataNamespace);
while (nested.Read())
{
switch (nested.DataKind)
{
case AtomDataKind.Feed:
feedEntries = new List();
nestedFeed = nested.CurrentFeed;
propertyValue = nestedFeed;
break;
case AtomDataKind.Entry:
nestedEntry = nested.CurrentEntry;
if (feedEntries != null)
{
feedEntries.Add(nestedEntry);
}
else
{
propertyValue = nestedEntry;
}
break;
case AtomDataKind.PagingLinks:
// Here the inner feed parser found a paging link, and stored it on nestedFeed.NextPageLink
// we are going to add it into a link table and associate
// with the collection at AtomMaterializer::Materialize()
// Do nothing for now.
break;
default:
throw new InvalidOperationException(Strings.AtomParser_UnexpectedContentUnderExpandedLink);
}
}
if (nestedFeed != null)
{
Debug.Assert(
nestedFeed.Entries == null,
"nestedFeed.Entries == null -- otherwise someone initialized this for us");
nestedFeed.Entries = feedEntries;
}
}
AtomContentProperty property = new AtomContentProperty();
property.Name = propertyName;
if (emptyInlineCollection || propertyValue == null)
{
property.IsNull = true;
if (isFeed)
{
property.Feed = new AtomFeed();
property.Feed.Entries = Enumerable.Empty();
}
else
{
property.Entry = new AtomEntry();
property.Entry.IsNull = true;
}
}
else
{
property.Feed = propertyValue as AtomFeed;
property.Entry = propertyValue as AtomEntry;
}
targetEntry.DataValues.Add(property);
}
}
///
/// Reads a property value and adds it as a text or a sub-property of
/// the specified .
///
/// Property to read content into.
private void ReadPropertyValueIntoResult(AtomContentProperty property)
{
Debug.Assert(this.reader != null, "reader != null");
Debug.Assert(property != null, "property != null");
switch (this.reader.NodeType)
{
case XmlNodeType.CDATA:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Text:
if (!String.IsNullOrEmpty(property.Text))
{
throw Error.InvalidOperation(Strings.Deserialize_MixedTextWithComment);
}
property.Text = this.reader.Value;
break;
case XmlNodeType.Comment:
case XmlNodeType.Whitespace:
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.EndElement:
// Do nothing.
// ProcessingInstruction, Whitespace would have thrown before
break;
case XmlNodeType.Element:
// We found an element while reading a property value. This should be
// a complex type.
if (!String.IsNullOrEmpty(property.Text))
{
throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
}
property.EnsureProperties();
AtomContentProperty prop = this.ReadPropertyValue();
if (prop != null)
{
property.Properties.Add(prop);
}
break;
default:
throw Error.InvalidOperation(Strings.Deserialize_ExpectingSimpleValue);
}
}
/// This method will read a string or a complex type.
/// The property value read.
/// Always checks for null attribute.
private AtomContentProperty ReadPropertyValue()
{
Debug.Assert(this.reader != null, "reader != null");
Debug.Assert(
this.reader.NodeType == XmlNodeType.Element,
"reader.NodeType == XmlNodeType.Element -- otherwise caller is confused as to where the reader is");
if (!this.IsDataWebElement)
{
// we expect ... only
SkipToEndAtDepth(this.reader, this.reader.Depth);
return null;
}
AtomContentProperty result = new AtomContentProperty();
result.Name = this.reader.LocalName;
result.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.DataWebMetadataNamespace);
result.IsNull = Util.DoesNullAttributeSayTrue(this.reader);
result.Text = result.IsNull ? null : String.Empty;
if (!this.reader.IsEmptyElement)
{
int depth = this.reader.Depth;
while (this.reader.Read())
{
this.ReadPropertyValueIntoResult(result);
if (this.reader.Depth == depth)
{
break;
}
}
}
return result;
}
///
/// Reads properties from the current reader into the
/// specified collection.
///
/// Values to read into.
private void ReadCurrentProperties(List values)
{
Debug.Assert(values != null, "values != null");
Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");
while (this.reader.Read())
{
if (ShouldIgnoreNode(this.reader))
{
continue;
}
if (this.reader.NodeType == XmlNodeType.EndElement)
{
return;
}
if (this.reader.NodeType == XmlNodeType.Element)
{
AtomContentProperty prop = this.ReadPropertyValue();
if (prop != null)
{
values.Add(prop);
}
}
}
}
///
/// Parses the current reader into a new
/// instance.
///
///
/// After invocation, the target entry that was created as a result
/// of parsing the current reader.
///
private void ParseCurrentEntry(out AtomEntry targetEntry)
{
Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");
// Push reader.
var callbackResult = this.entryCallback(this.reader);
Debug.Assert(callbackResult.Key != null, "callbackResult.Key != null");
this.readers.Push(this.reader);
this.reader = callbackResult.Key;
this.reader.Read();
Debug.Assert(this.reader.LocalName == "entry", "this.reader.LocalName == 'entry' - otherwise we're not reading the subtree");
bool hasContent = false;
targetEntry = new AtomEntry();
targetEntry.DataValues = new List();
targetEntry.Tag = callbackResult.Value;
targetEntry.ETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);
while (this.reader.Read())
{
if (ShouldIgnoreNode(this.reader))
{
continue;
}
if (this.reader.NodeType == XmlNodeType.Element)
{
int depth = this.reader.Depth;
string elementName = this.reader.LocalName;
string namespaceURI = this.reader.NamespaceURI;
if (namespaceURI == XmlConstants.AtomNamespace)
{
if (elementName == XmlConstants.AtomCategoryElementName && targetEntry.TypeName == null)
{
string text = this.reader.GetAttributeEx(XmlConstants.AtomCategorySchemeAttributeName, XmlConstants.AtomNamespace);
if (text == this.typeScheme)
{
targetEntry.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomCategoryTermAttributeName, XmlConstants.AtomNamespace);
}
}
else if (elementName == XmlConstants.AtomContentElementName)
{
hasContent = true;
this.ParseCurrentContent(targetEntry);
}
else if (elementName == XmlConstants.AtomIdElementName && targetEntry.Identity == null)
{
// The .Identity == null check ensures that only the first id element is processed.
string idText = ReadElementStringForText(this.reader);
idText = Util.ReferenceIdentity(idText);
// here we could just assign idText to Identity
// however we used to check for AbsoluteUri, thus we need to
// convert string to Uri and check for absoluteness
Uri idUri = Util.CreateUri(idText, UriKind.RelativeOrAbsolute);
if (!idUri.IsAbsoluteUri)
{
throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri);
}
targetEntry.Identity = idText;
}
else if (elementName == XmlConstants.AtomLinkElementName)
{
this.ParseCurrentLink(targetEntry);
}
}
else if (namespaceURI == XmlConstants.DataWebMetadataNamespace)
{
if (elementName == XmlConstants.AtomPropertiesElementName)
{
if (targetEntry.MediaLinkEntry.HasValue && !targetEntry.MediaLinkEntry.Value)
{
// This means we saw a non-empty element but now we have a Properties element
// that also carries properties
throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
}
targetEntry.MediaLinkEntry = true;
if (!this.reader.IsEmptyElement)
{
this.ReadCurrentProperties(targetEntry.DataValues);
}
}
}
SkipToEndAtDepth(this.reader, depth);
}
}
if (targetEntry.Identity == null)
{
throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
}
if (!hasContent)
{
// Content is expected for the GetResponse operation
throw Error.BatchStreamContentExpected(BatchStreamState.GetResponse);
}
this.reader = this.readers.Pop();
}
/// Parses the value for the current feed count.
/// This method will update the value on the current feed.
private void ParseCurrentFeedCount()
{
if (this.feed == null)
{
throw new InvalidOperationException(Strings.AtomParser_FeedCountNotUnderFeed);
}
if (this.feed.Count.HasValue)
{
throw new InvalidOperationException(Strings.AtomParser_ManyFeedCounts);
}
long countValue;
if (!long.TryParse(MaterializeAtom.ReadElementString(this.reader, true), System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out countValue))
{
throw new FormatException(Strings.MaterializeFromAtom_CountFormatError);
}
if (countValue < 0)
{
throw new FormatException(Strings.MaterializeFromAtom_CountFormatError);
}
this.feed.Count = countValue;
}
///
/// Parsing paging links
///
private void ParseCurrentFeedPagingLinks()
{
// feed should never be null here since there is an outer check
// just need to assert
Debug.Assert(this.feed != null, "Trying to parser paging links but feed is null.");
if (this.feed.NextLink != null)
{
// we have set next link before, this is a duplicate
// atom spec does not allow duplicated next links
throw new InvalidOperationException(Strings.AtomMaterializer_DuplicatedNextLink);
}
string nextLink = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
if (nextLink == null)
{
throw new InvalidOperationException(Strings.AtomMaterializer_LinksMissingHref);
}
else
{
this.feed.NextLink = this.ConvertHRefAttributeValueIntoURI(nextLink);
}
}
///
/// creates a new uri instance which takes into account the base uri of the reader.
///
/// href attribute value.
/// a new instance of uri as refered by the
private Uri ConvertHRefAttributeValueIntoURI(string href)
{
Uri uri = Util.CreateUri(href, UriKind.RelativeOrAbsolute);
if (!uri.IsAbsoluteUri && !String.IsNullOrEmpty(this.reader.BaseURI))
{
Uri baseUri = Util.CreateUri(this.reader.BaseURI, UriKind.RelativeOrAbsolute);
// The reason why we can't use Util.CreateUri function here, is that the util method
// checks for trailing slashes in the baseuri and starting forward slashes in the request uri
// and does some tricks which is not consistent with the uri class behaviour. Hence using the
// uri class directly here.
uri = new Uri(baseUri, uri);
}
return uri;
}
#endregion Private methods.
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MulticastDelegate.cs
- FillErrorEventArgs.cs
- WrapPanel.cs
- CqlParser.cs
- DataGridItem.cs
- BinaryParser.cs
- DBSqlParser.cs
- SQLCharsStorage.cs
- RuntimeConfig.cs
- DataControlFieldCollection.cs
- AvTraceFormat.cs
- XsltLibrary.cs
- PolicyLevel.cs
- XmlnsCache.cs
- RegexTypeEditor.cs
- MethodImplAttribute.cs
- TextOnlyOutput.cs
- SingleAnimationBase.cs
- ServiceModelEnumValidatorAttribute.cs
- ApplicationDirectoryMembershipCondition.cs
- Int64.cs
- PersonalizationProviderCollection.cs
- NetStream.cs
- ObjectToIdCache.cs
- ResourceDescriptionAttribute.cs
- DispatcherHookEventArgs.cs
- CompilerCollection.cs
- ClientConfigurationHost.cs
- EventRouteFactory.cs
- SettingsPropertyIsReadOnlyException.cs
- GraphicsContainer.cs
- CqlLexer.cs
- ConfigXmlElement.cs
- AutomationPropertyInfo.cs
- TdsValueSetter.cs
- Header.cs
- DataGridViewTextBoxEditingControl.cs
- ProfileParameter.cs
- Sql8ExpressionRewriter.cs
- CommonBehaviorsSection.cs
- ValidationHelper.cs
- NullRuntimeConfig.cs
- MarshalDirectiveException.cs
- MissingFieldException.cs
- XamlStyleSerializer.cs
- XmlSchemaSimpleTypeList.cs
- TableCellCollection.cs
- VisualCollection.cs
- VideoDrawing.cs
- SymbolType.cs
- XhtmlBasicLinkAdapter.cs
- SmiEventSink.cs
- PrePrepareMethodAttribute.cs
- SQLChars.cs
- InvalidProgramException.cs
- BindingFormattingDialog.cs
- SettingsBase.cs
- Material.cs
- Compiler.cs
- TextStore.cs
- XmlUtil.cs
- AppendHelper.cs
- FormClosedEvent.cs
- ResourceManager.cs
- ListViewGroup.cs
- CodeIndexerExpression.cs
- NavigateEvent.cs
- TextCompositionEventArgs.cs
- ResourcesChangeInfo.cs
- SiteMembershipCondition.cs
- SQLMoney.cs
- HtmlTable.cs
- ReferenceConverter.cs
- WebServiceHost.cs
- NativeObjectSecurity.cs
- ContainerFilterService.cs
- ConfigXmlSignificantWhitespace.cs
- DesignerToolboxInfo.cs
- MachineKeySection.cs
- CodeRemoveEventStatement.cs
- MessageHeaderException.cs
- ErrorRuntimeConfig.cs
- FocusChangedEventArgs.cs
- SizeAnimation.cs
- CapabilitiesState.cs
- XmlNodeChangedEventManager.cs
- WsatRegistrationHeader.cs
- ExpressionConverter.cs
- ActivityExecutionContext.cs
- ApplicationDirectory.cs
- XslVisitor.cs
- MethodImplAttribute.cs
- LogicalExpr.cs
- BitmapEffectState.cs
- URLString.cs
- MbpInfo.cs
- TemplateBindingExtensionConverter.cs
- InkCanvasInnerCanvas.cs
- _Events.cs
- CommonProperties.cs