SyndicationDeserializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / SyndicationDeserializer.cs / 3 / SyndicationDeserializer.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a deserializer for atom content structured as
//      syndication items or feeds. 
//  
//
// @owner [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Serializers
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Linq;
    using System.IO;
    using System.ServiceModel.Syndication; 
    using System.Text;
    using System.Xml; 
 
    #endregion Namespaces.
 
    /// Provides a deserializer for structured content.
    internal class SyndicationDeserializer : Deserializer
    {
        /// Factory for syndication formatting objects. 
        private readonly SyndicationFormatterFactory factory;
 
        /// reader to read xml from the request stream 
        private readonly XmlReader xmlReader;
 
        /// Initializes a new  for the specified stream.
        /// Input stream reader from which ATOM content must be read.
        /// Encoding to use for the stream (null to auto-discover).
        /// Data service for which the deserializer will act. 
        /// indicates whether this is a update operation or not
        /// Factory for formatter objects. 
        /// Tracker to use for modifications. 
        internal SyndicationDeserializer(Stream stream, Encoding encoding, IDataService dataService, bool update, SyndicationFormatterFactory factory, UpdateTracker tracker)
            : base(update, dataService, tracker) 
        {
            Debug.Assert(stream != null, "stream != null");
            Debug.Assert(factory != null, "factory != null");
 
            this.factory = factory;
            this.xmlReader = factory.CreateReader(stream, encoding); 
        } 

        ///  
        /// Indicates the various form of data in the inline xml element
        /// 
        private enum LinkContent
        { 
            /// If the link element didn't not contain an inline element at all.
            NoInlineElementSpecified, 
 
            /// If the link element contained an empty inline element.
            EmptyInlineElementSpecified, 

            /// If the inline element under the link element contained some data.
            InlineElementContainsData
        } 

        /// returns the content format for the deserializer 
        protected override ContentFormat ContentFormat 
        {
            get 
            {
                return ContentFormat.Atom;
            }
        } 

        ///  
        /// Assumes the payload to represent a single object and processes accordingly 
        /// 
        /// info about the object being created 
        /// the newly formed object that the payload represents
        protected override object CreateSingleObject(SegmentInfo segmentInfo)
        {
            SyndicationItem item = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), this.xmlReader); 
            return this.CreateObject(segmentInfo, true /*topLevel*/, item);
        } 
 
        /// Provides an opportunity to clean-up resources.
        ///  
        /// Whether the call is being made from an explicit call to
        /// IDisposable.Dispose() rather than through the finalizer.
        /// 
        protected override void Dispose(bool disposing) 
        {
            base.Dispose(disposing); 
            if (disposing) 
            {
                this.xmlReader.Close(); 
            }
        }

        ///  
        /// Get the resource referred by the uri in the payload
        ///  
        /// resource referred by the uri in the payload. 
        protected override string GetLinkUriFromPayload()
        { 
            throw Error.NotImplemented();
        }

        /// Checks whether the specified item has a payload. 
        /// Item to check.
        /// true if the item has content or links specified; false otherwise. 
        private static bool HasContent(SyndicationItem item) 
        {
            return item.Content != null || item.Links.Count > 0; 
        }

        /// Gets the text for the type annotated on the specified .
        /// Item to read type from. 
        /// The text for the type annotated on the specified item; null if none is set.
        private static string SyndicationItemGetType(SyndicationItem item) 
        { 
            Debug.Assert(item != null, "item != null");
            SyndicationCategory category = item.Categories.Where(c => c.Scheme == XmlConstants.DataWebSchemeNamespace).FirstOrDefault(); 
            return (category == null) ? null : category.Name;
        }

        /// Reads a SyndicationFeed object from the specified XmlReader. 
        /// Formatter to use when reading content.
        /// Read to read feed from. 
        /// A new SyndicationFeed instance. 
        private static SyndicationFeed ReadSyndicationFeed(SyndicationFeedFormatter formatter, XmlReader reader)
        { 
            Debug.Assert(formatter != null, "formatter != null");
            Debug.Assert(reader != null, "reader != null");

            try 
            {
                formatter.ReadFrom(reader); 
            } 
            catch (XmlException exception)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingFeed(exception.Message), exception);
            }

            Debug.Assert(formatter.Feed != null, "formatter.Feed != null"); 
            return formatter.Feed;
        } 
 
        /// Reads a SyndicationItem object from the specified XmlReader.
        /// Formatter to use when reading content. 
        /// Read to read feed from.
        /// A new SyndicationItem instance.
        private static SyndicationItem ReadSyndicationItem(SyndicationItemFormatter formatter, XmlReader reader)
        { 
            Debug.Assert(formatter != null, "formatter != null");
            Debug.Assert(reader != null, "reader != null"); 
 
            try
            { 
                formatter.ReadFrom(reader);
            }
            catch (XmlException exception)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingEntry(exception.Message), exception);
            } 
 
            Debug.Assert(formatter.Item != null, "formatter.Item != null");
            return formatter.Item; 
        }

        /// 
        /// Read the link media type and validate for non open property types 
        /// 
        /// media type as specified on the link element. 
        /// property which the link represents. 
        /// returns the type parameters specified in the media link.
        private static string ValidateTypeParameterForNonOpenTypeProperties(string mediaType, ResourceProperty property) 
        {
            string typeParameterValue = null;
            if (!String.IsNullOrEmpty(mediaType))
            { 
                string mime;
                Encoding encoding; 
                KeyValuePair[] contentTypeParameters = HttpProcessUtility.ReadContentType(mediaType, out mime, out encoding); 
                if (mime != XmlConstants.MimeApplicationAtom)
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MimeTypeMustBeApplicationAtom(mime, XmlConstants.MimeApplicationAtom));
                }

                // If the type parameter is specified, make sure its correct. We do the validation for known properties here 
                // and for open-properties, the validation is done if the link is expanded. Otherwise, there is no good way of
                // doing the validation. 
                typeParameterValue = HttpProcessUtility.GetParameterValue(contentTypeParameters, XmlConstants.AtomTypeAttributeName); 
                if (!String.IsNullOrEmpty(typeParameterValue) && property != null)
                { 
                    if (property.Kind == ResourcePropertyKind.ResourceReference)
                    {
                        if (typeParameterValue != XmlConstants.AtomEntryElementName)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
                        } 
                    } 
                    else if (typeParameterValue != XmlConstants.AtomFeedElementName)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
                    }
                }
            } 

            return typeParameterValue; 
        } 

        /// Applies the properties in the plain XML content to the specified resource. 
        /// Content to read values from.
        /// Type of resource whose values are being set.
        /// Target resource.
        private void ApplyContent(XmlSyndicationContent content, ResourceType resourceType, object resource) 
        {
            Debug.Assert(content != null, "content != null"); 
            using (XmlReader reader = content.GetReaderAtContent()) 
            {
                WebUtil.XmlReaderEnsureElement(reader); 
                Debug.Assert(
                    reader.NodeType == XmlNodeType.Element,
                    reader.NodeType.ToString() + " == XmlNodeType.Element -- otherwise XmlSyndicationContent didn't see a 'content' tag");
 
                reader.ReadStartElement(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace);
                reader.ReadStartElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace); 
                PlainXmlDeserializer.ApplyContent(this, reader, resourceType, resource, this.MaxObjectCount); 
            }
        } 

        /// Reads the current object from the .
        /// segmentinfo containing information about the current element that is getting processes
        /// true if the element currently pointed by the xml reader refers to a top level element 
        /// Item to read from.
        /// returns the clr object with the data populated 
        private object CreateObject(SegmentInfo segmentInfo, bool topLevel, SyndicationItem item) 
        {
            Debug.Assert(item != null, "item != null"); 
            Debug.Assert(topLevel || !this.Update, "deep updates not supported");

            this.RecurseEnter();
            object result; 

            // update the object count everytime you encounter a new resource 
            this.CheckAndIncrementObjectCount(); 

            // Process the type annotation. 
            ResourceType currentResourceType = this.GetResourceType(item, segmentInfo.TargetElementType);
            if (currentResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_OnlyEntityTypesMustBeSpecifiedInEntryElement(currentResourceType.FullName)); 
            }
 
            // Get a resource cookie from the provider. 
            ResourceContainer container;
#if ASTORIA_OPEN_OBJECT 
            if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
            {
                container = this.Service.Provider.GetContainerForResourceType(currentResourceType.Type);
            } 
            else
#endif 
            { 
                Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "segmentInfo.TargetKind == RequestTargetKind.Resource");
                container = segmentInfo.TargetContainer; 
            }

            if (this.Update)
            { 
                Debug.Assert(currentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "only expecting entity types");
                bool verifyETag = topLevel && HasContent(item); 
                bool replaceResource = topLevel && this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT; 

                // if its a top level resource, then it cannot be null 
                result = this.CreateObjectFromUri(currentResourceType, segmentInfo, (RequestDescription)null, verifyETag, topLevel /*checkForNull*/, replaceResource);
                if (this.Tracker != null)
                {
                    this.Tracker.TrackAction(result, container, UpdateOperations.Change); 
                }
            } 
            else 
            {
                if (segmentInfo.TargetKind == RequestTargetKind.Resource) 
                {
                    this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend);
                }
 
                result = this.Service.Provider.CreateResource(container.Name, currentResourceType.FullName);
                if (this.Tracker != null) 
                { 
                    this.Tracker.TrackAction(result, container, UpdateOperations.Add);
                } 
            }

            // Process the content in the entry.
            if (item.Content != null) 
            {
                string contentType = item.Content.Type; 
                if (contentType != XmlConstants.MimeApplicationXml) 
                {
                    throw DataServiceException.CreateBadRequestError( 
                        Strings.Syndication_EntryContentTypeUnsupported(contentType));
                }

                XmlSyndicationContent itemContent = item.Content as XmlSyndicationContent; 
                Debug.Assert(itemContent != null, "itemContent != null -- otherwise formatter created the wrong type.");
                this.ApplyContent(itemContent, currentResourceType, result); 
            } 

            // Process the links in the entry. 
            foreach (SyndicationLink link in item.Links)
            {
                string navigationPropertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(link.RelationshipType);
 
                if (null == navigationPropertyName)
                { 
                    continue; 
                }
 
                Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb);
                this.ApplyLink(link, currentResourceType, result, navigationPropertyName);
            }
 
            this.RecurseLeave();
            return result; 
        } 

        /// Applies the information from a link to the specified resource. 
        /// LinkDescriptor with information to apply.
        /// Type for the target resource.
        /// Target resource to which information will be applied.
        /// Name of the property that this link represents. 
        private void ApplyLink(SyndicationLink link, ResourceType resourceType, object resource, string propertyName)
        { 
            Debug.Assert(link != null, "link != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resource != null, "resource != null"); 

            ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
            if (
#if ASTORIA_OPEN_OBJECT 
                (property == null && resourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen) ||
#else 
                property == null || 
#endif
                (property != null && property.TypeKind != ResourceTypeKind.EntityType)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidNavigationPropertyName(propertyName, resourceType.FullName));
            }
 
            string typeParameterValue = ValidateTypeParameterForNonOpenTypeProperties(link.MediaType, property);
            LinkContent linkContent = this.HandleLinkContent(link, resource, property, typeParameterValue, propertyName); 
 
            #region Handle bind/unbind operation
            // If the href was specified empty or an empty inline element was specified, then we will set the 
            // reference to null - this helps in overrriding if there was a default non-value for this property
            // else if only link element was specified, and then href points to a single result, then we will
            // perform a bind operation
            if ((linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && String.IsNullOrEmpty(link.Uri.OriginalString)) || 
                linkContent == LinkContent.EmptyInlineElementSpecified)
            { 
                // update the object count when you are performing a bind operation 
                this.CheckAndIncrementObjectCount();
                if (property != null && property.Kind == ResourcePropertyKind.ResourceSetReference) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
                }
 
                // For open properties, we will assume that this is a reference property and set it to null
                this.Service.Provider.SetReference(resource, propertyName, null); 
            } 
            else if (linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && !String.IsNullOrEmpty(link.Uri.OriginalString))
            { 
                // update the object count when you are performing a bind operation
                this.CheckAndIncrementObjectCount();

                // If the link points to a reference navigation property, then update the link 
                Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(link.Uri.OriginalString, this.Service.RequestParams);
                RequestDescription description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service); 
                if (!description.IsSingleResult) 
                {
                    if (property != null && property.Kind == ResourcePropertyKind.ResourceReference) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_LinkHrefMustReferToSingleResource(propertyName));
                    }
 
                    return;
                } 
 
                // no need to check for null. For collection properties, they can never be null and that
                // check has been added below. For reference properties, if they are null, it means unbind 
                // and hence no need to check for null.
                object propertyItem = CreateObjectFromUri(null, null, description, false /*verifyETag*/, false /*checkForNull*/, false /*replaceResource*/);
                if (property != null)
                { 
                    if (property.Kind == ResourcePropertyKind.ResourceReference)
                    { 
                        this.Service.Provider.SetReference(resource, propertyName, propertyItem); 
                    }
                    else 
                    {
                        WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
                        this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
                    } 
                }
                else if (typeParameterValue == null) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingTypeParameterOnLinkElement(propertyName));
                } 
                else if (typeParameterValue == XmlConstants.AtomEntryElementName)
                {
                    this.Service.Provider.SetReference(resource, propertyName, propertyItem);
                } 
                else
                { 
                    if (typeParameterValue != XmlConstants.AtomFeedElementName) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterInTypeAttributeInLink(propertyName, typeParameterValue)); 
                    }

                    WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
                    this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem); 
                }
            } 
            #endregion Handle bind/unbind operation 
        }
 
        /// Gets the type attribute and resolves the type.
        /// Item from which type attribute needs to be read
        /// Expected base type for the item.
        /// Resolved type. 
        private ResourceType GetResourceType(SyndicationItem item, Type expectedType)
        { 
            Debug.Assert(item != null, "item != null"); 
            string typeName = SyndicationItemGetType(item);
            ResourceType resourceType; 

            // If the type is not specified in the payload, we assume the type to be the expected type
            if (String.IsNullOrEmpty(typeName))
            { 
                // check if the expected type takes part in inheritance
                resourceType = this.Service.Provider.GetResourceType(expectedType); 
                if (resourceType.HasDerivedTypes) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); 
                }
            }
            else
            { 
                // Otherwise, try and resolve the name specified in the payload
                resourceType = this.Service.Provider.TryResolveTypeName(typeName); 
                if (resourceType == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName)); 
                }
            }

            return resourceType; 
        }
 
        ///  
        /// Handle the contents under the link element
        ///  
        /// syndication link element
        /// parent resource which contains the link.
        /// property representing the link.
        /// type parameter value as specified in the type attribute. 
        /// name of the property that this link represents.
        /// returns whether there are child elements under link element. 
        private LinkContent HandleLinkContent( 
            SyndicationLink link,
            object parentResource, 
            ResourceProperty property,
            string typeParameterValue,
            string propertyName)
        { 
            Debug.Assert(parentResource != null, "parent resource cannot be null");
            Debug.Assert(link != null, "link != null"); 
 
            LinkContent linkContent = LinkContent.NoInlineElementSpecified;
            foreach (var e in link.ElementExtensions) 
            {
                // link can contain other elements apart from the inline elements.
                if (e.OuterNamespace != XmlConstants.DataWebMetadataNamespace ||
                    e.OuterName != XmlConstants.AtomInlineElementName) 
                {
                    continue; 
                } 

                // Deep payload cannot be specified for update 
                if (this.Update)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported);
                } 

                linkContent = LinkContent.EmptyInlineElementSpecified; 
                using (XmlReader linkReader = e.GetReader()) 
                {
                    while (linkReader.Read()) 
                    {
                        if (linkReader.NodeType == XmlNodeType.Element)
                        {
                            string elementName = linkReader.LocalName; 
                            string namespaceUri = linkReader.NamespaceURI;
                            if (namespaceUri != XmlConstants.AtomNamespace) 
                            { 
                                throw DataServiceException.CreateBadRequestError(
                                    Strings.BadRequest_InlineElementMustContainValidElement( 
                                        elementName,
                                        XmlConstants.AtomInlineElementName,
                                        XmlConstants.AtomFeedElementName,
                                        XmlConstants.AtomEntryElementName)); 
                            }
 
                            linkContent = LinkContent.InlineElementContainsData; 
                            if (elementName == XmlConstants.AtomEntryElementName)
                            { 
                                if (property != null)
                                {
                                    if (property.Kind != ResourcePropertyKind.ResourceReference)
                                    { 
                                        throw DataServiceException.CreateBadRequestError(Strings.Syndication_EntryElementForReferenceProperties(e.OuterName, propertyName));
                                    } 
                                } 
                                else if (typeParameterValue != null && typeParameterValue != elementName)
                                { 
                                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
                                }

                                // Make sure if the media type is specified. If its specified, it should better be link 
                                SyndicationItem propertyItem;
                                propertyItem = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), linkReader); 
 
                                SegmentInfo propertySegment = CreateSegment(property, propertyName, true /* singleResult */);
                                object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, propertyItem); 
                                this.Service.Provider.SetReference(parentResource, propertyName, propertyValue);
                            }
                            else if (elementName == XmlConstants.AtomFeedElementName)
                            { 
                                if (property != null)
                                { 
                                    if (property.Kind != ResourcePropertyKind.ResourceSetReference) 
                                    {
                                        throw DataServiceException.CreateBadRequestError(Strings.Syndication_FeedElementForCollections(e.OuterName, propertyName)); 
                                    }
                                }
                                else if (typeParameterValue != null && typeParameterValue != elementName)
                                { 
                                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
                                } 
 
                                SyndicationFeed propertyFeed;
                                propertyFeed = ReadSyndicationFeed(this.factory.CreateSyndicationFeedFormatter(), linkReader); 

                                SegmentInfo propertySegment = CreateSegment(property, propertyName, false /* singleResult */);
                                foreach (SyndicationItem item in propertyFeed.Items)
                                { 
                                    object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, item);
                                    if (propertyValue == null) 
                                    { 
                                        if (propertySegment.ProjectedProperty != null &&
                                            propertySegment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference) 
                                        {
                                            throw DataServiceException.CreateBadRequestError(
                                                Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
                                        } 
                                    }
 
                                    Debug.Assert( 
#if ASTORIA_OPEN_OBJECT
                                        propertySegment.TargetKind == RequestTargetKind.OpenProperty || 
#endif
                                        (propertySegment.TargetSource == RequestTargetSource.Property && propertySegment.TargetKind == RequestTargetKind.Resource && propertySegment.SingleResult == false),
                                        "must be navigation set property or open property");
 
                                    this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue);
                                } 
                            } 
                            else
                            { 
                                throw DataServiceException.CreateBadRequestError(
                                    Strings.BadRequest_InlineElementMustContainValidElement(
                                        elementName,
                                        XmlConstants.AtomInlineElementName, 
                                        XmlConstants.AtomFeedElementName,
                                        XmlConstants.AtomEntryElementName)); 
                            } 
                        }
                    } 
                }
            }

            return linkContent; 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a deserializer for atom content structured as
//      syndication items or feeds. 
//  
//
// @owner [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Serializers
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Linq;
    using System.IO;
    using System.ServiceModel.Syndication; 
    using System.Text;
    using System.Xml; 
 
    #endregion Namespaces.
 
    /// Provides a deserializer for structured content.
    internal class SyndicationDeserializer : Deserializer
    {
        /// Factory for syndication formatting objects. 
        private readonly SyndicationFormatterFactory factory;
 
        /// reader to read xml from the request stream 
        private readonly XmlReader xmlReader;
 
        /// Initializes a new  for the specified stream.
        /// Input stream reader from which ATOM content must be read.
        /// Encoding to use for the stream (null to auto-discover).
        /// Data service for which the deserializer will act. 
        /// indicates whether this is a update operation or not
        /// Factory for formatter objects. 
        /// Tracker to use for modifications. 
        internal SyndicationDeserializer(Stream stream, Encoding encoding, IDataService dataService, bool update, SyndicationFormatterFactory factory, UpdateTracker tracker)
            : base(update, dataService, tracker) 
        {
            Debug.Assert(stream != null, "stream != null");
            Debug.Assert(factory != null, "factory != null");
 
            this.factory = factory;
            this.xmlReader = factory.CreateReader(stream, encoding); 
        } 

        ///  
        /// Indicates the various form of data in the inline xml element
        /// 
        private enum LinkContent
        { 
            /// If the link element didn't not contain an inline element at all.
            NoInlineElementSpecified, 
 
            /// If the link element contained an empty inline element.
            EmptyInlineElementSpecified, 

            /// If the inline element under the link element contained some data.
            InlineElementContainsData
        } 

        /// returns the content format for the deserializer 
        protected override ContentFormat ContentFormat 
        {
            get 
            {
                return ContentFormat.Atom;
            }
        } 

        ///  
        /// Assumes the payload to represent a single object and processes accordingly 
        /// 
        /// info about the object being created 
        /// the newly formed object that the payload represents
        protected override object CreateSingleObject(SegmentInfo segmentInfo)
        {
            SyndicationItem item = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), this.xmlReader); 
            return this.CreateObject(segmentInfo, true /*topLevel*/, item);
        } 
 
        /// Provides an opportunity to clean-up resources.
        ///  
        /// Whether the call is being made from an explicit call to
        /// IDisposable.Dispose() rather than through the finalizer.
        /// 
        protected override void Dispose(bool disposing) 
        {
            base.Dispose(disposing); 
            if (disposing) 
            {
                this.xmlReader.Close(); 
            }
        }

        ///  
        /// Get the resource referred by the uri in the payload
        ///  
        /// resource referred by the uri in the payload. 
        protected override string GetLinkUriFromPayload()
        { 
            throw Error.NotImplemented();
        }

        /// Checks whether the specified item has a payload. 
        /// Item to check.
        /// true if the item has content or links specified; false otherwise. 
        private static bool HasContent(SyndicationItem item) 
        {
            return item.Content != null || item.Links.Count > 0; 
        }

        /// Gets the text for the type annotated on the specified .
        /// Item to read type from. 
        /// The text for the type annotated on the specified item; null if none is set.
        private static string SyndicationItemGetType(SyndicationItem item) 
        { 
            Debug.Assert(item != null, "item != null");
            SyndicationCategory category = item.Categories.Where(c => c.Scheme == XmlConstants.DataWebSchemeNamespace).FirstOrDefault(); 
            return (category == null) ? null : category.Name;
        }

        /// Reads a SyndicationFeed object from the specified XmlReader. 
        /// Formatter to use when reading content.
        /// Read to read feed from. 
        /// A new SyndicationFeed instance. 
        private static SyndicationFeed ReadSyndicationFeed(SyndicationFeedFormatter formatter, XmlReader reader)
        { 
            Debug.Assert(formatter != null, "formatter != null");
            Debug.Assert(reader != null, "reader != null");

            try 
            {
                formatter.ReadFrom(reader); 
            } 
            catch (XmlException exception)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingFeed(exception.Message), exception);
            }

            Debug.Assert(formatter.Feed != null, "formatter.Feed != null"); 
            return formatter.Feed;
        } 
 
        /// Reads a SyndicationItem object from the specified XmlReader.
        /// Formatter to use when reading content. 
        /// Read to read feed from.
        /// A new SyndicationItem instance.
        private static SyndicationItem ReadSyndicationItem(SyndicationItemFormatter formatter, XmlReader reader)
        { 
            Debug.Assert(formatter != null, "formatter != null");
            Debug.Assert(reader != null, "reader != null"); 
 
            try
            { 
                formatter.ReadFrom(reader);
            }
            catch (XmlException exception)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.Syndication_ErrorReadingEntry(exception.Message), exception);
            } 
 
            Debug.Assert(formatter.Item != null, "formatter.Item != null");
            return formatter.Item; 
        }

        /// 
        /// Read the link media type and validate for non open property types 
        /// 
        /// media type as specified on the link element. 
        /// property which the link represents. 
        /// returns the type parameters specified in the media link.
        private static string ValidateTypeParameterForNonOpenTypeProperties(string mediaType, ResourceProperty property) 
        {
            string typeParameterValue = null;
            if (!String.IsNullOrEmpty(mediaType))
            { 
                string mime;
                Encoding encoding; 
                KeyValuePair[] contentTypeParameters = HttpProcessUtility.ReadContentType(mediaType, out mime, out encoding); 
                if (mime != XmlConstants.MimeApplicationAtom)
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MimeTypeMustBeApplicationAtom(mime, XmlConstants.MimeApplicationAtom));
                }

                // If the type parameter is specified, make sure its correct. We do the validation for known properties here 
                // and for open-properties, the validation is done if the link is expanded. Otherwise, there is no good way of
                // doing the validation. 
                typeParameterValue = HttpProcessUtility.GetParameterValue(contentTypeParameters, XmlConstants.AtomTypeAttributeName); 
                if (!String.IsNullOrEmpty(typeParameterValue) && property != null)
                { 
                    if (property.Kind == ResourcePropertyKind.ResourceReference)
                    {
                        if (typeParameterValue != XmlConstants.AtomEntryElementName)
                        { 
                            throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
                        } 
                    } 
                    else if (typeParameterValue != XmlConstants.AtomFeedElementName)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
                    }
                }
            } 

            return typeParameterValue; 
        } 

        /// Applies the properties in the plain XML content to the specified resource. 
        /// Content to read values from.
        /// Type of resource whose values are being set.
        /// Target resource.
        private void ApplyContent(XmlSyndicationContent content, ResourceType resourceType, object resource) 
        {
            Debug.Assert(content != null, "content != null"); 
            using (XmlReader reader = content.GetReaderAtContent()) 
            {
                WebUtil.XmlReaderEnsureElement(reader); 
                Debug.Assert(
                    reader.NodeType == XmlNodeType.Element,
                    reader.NodeType.ToString() + " == XmlNodeType.Element -- otherwise XmlSyndicationContent didn't see a 'content' tag");
 
                reader.ReadStartElement(XmlConstants.AtomContentElementName, XmlConstants.AtomNamespace);
                reader.ReadStartElement(XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace); 
                PlainXmlDeserializer.ApplyContent(this, reader, resourceType, resource, this.MaxObjectCount); 
            }
        } 

        /// Reads the current object from the .
        /// segmentinfo containing information about the current element that is getting processes
        /// true if the element currently pointed by the xml reader refers to a top level element 
        /// Item to read from.
        /// returns the clr object with the data populated 
        private object CreateObject(SegmentInfo segmentInfo, bool topLevel, SyndicationItem item) 
        {
            Debug.Assert(item != null, "item != null"); 
            Debug.Assert(topLevel || !this.Update, "deep updates not supported");

            this.RecurseEnter();
            object result; 

            // update the object count everytime you encounter a new resource 
            this.CheckAndIncrementObjectCount(); 

            // Process the type annotation. 
            ResourceType currentResourceType = this.GetResourceType(item, segmentInfo.TargetElementType);
            if (currentResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_OnlyEntityTypesMustBeSpecifiedInEntryElement(currentResourceType.FullName)); 
            }
 
            // Get a resource cookie from the provider. 
            ResourceContainer container;
#if ASTORIA_OPEN_OBJECT 
            if (segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
            {
                container = this.Service.Provider.GetContainerForResourceType(currentResourceType.Type);
            } 
            else
#endif 
            { 
                Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.Resource, "segmentInfo.TargetKind == RequestTargetKind.Resource");
                container = segmentInfo.TargetContainer; 
            }

            if (this.Update)
            { 
                Debug.Assert(currentResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "only expecting entity types");
                bool verifyETag = topLevel && HasContent(item); 
                bool replaceResource = topLevel && this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT; 

                // if its a top level resource, then it cannot be null 
                result = this.CreateObjectFromUri(currentResourceType, segmentInfo, (RequestDescription)null, verifyETag, topLevel /*checkForNull*/, replaceResource);
                if (this.Tracker != null)
                {
                    this.Tracker.TrackAction(result, container, UpdateOperations.Change); 
                }
            } 
            else 
            {
                if (segmentInfo.TargetKind == RequestTargetKind.Resource) 
                {
                    this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend);
                }
 
                result = this.Service.Provider.CreateResource(container.Name, currentResourceType.FullName);
                if (this.Tracker != null) 
                { 
                    this.Tracker.TrackAction(result, container, UpdateOperations.Add);
                } 
            }

            // Process the content in the entry.
            if (item.Content != null) 
            {
                string contentType = item.Content.Type; 
                if (contentType != XmlConstants.MimeApplicationXml) 
                {
                    throw DataServiceException.CreateBadRequestError( 
                        Strings.Syndication_EntryContentTypeUnsupported(contentType));
                }

                XmlSyndicationContent itemContent = item.Content as XmlSyndicationContent; 
                Debug.Assert(itemContent != null, "itemContent != null -- otherwise formatter created the wrong type.");
                this.ApplyContent(itemContent, currentResourceType, result); 
            } 

            // Process the links in the entry. 
            foreach (SyndicationLink link in item.Links)
            {
                string navigationPropertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(link.RelationshipType);
 
                if (null == navigationPropertyName)
                { 
                    continue; 
                }
 
                Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb);
                this.ApplyLink(link, currentResourceType, result, navigationPropertyName);
            }
 
            this.RecurseLeave();
            return result; 
        } 

        /// Applies the information from a link to the specified resource. 
        /// LinkDescriptor with information to apply.
        /// Type for the target resource.
        /// Target resource to which information will be applied.
        /// Name of the property that this link represents. 
        private void ApplyLink(SyndicationLink link, ResourceType resourceType, object resource, string propertyName)
        { 
            Debug.Assert(link != null, "link != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resource != null, "resource != null"); 

            ResourceProperty property = resourceType.TryResolvePropertyName(propertyName);
            if (
#if ASTORIA_OPEN_OBJECT 
                (property == null && resourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen) ||
#else 
                property == null || 
#endif
                (property != null && property.TypeKind != ResourceTypeKind.EntityType)) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidNavigationPropertyName(propertyName, resourceType.FullName));
            }
 
            string typeParameterValue = ValidateTypeParameterForNonOpenTypeProperties(link.MediaType, property);
            LinkContent linkContent = this.HandleLinkContent(link, resource, property, typeParameterValue, propertyName); 
 
            #region Handle bind/unbind operation
            // If the href was specified empty or an empty inline element was specified, then we will set the 
            // reference to null - this helps in overrriding if there was a default non-value for this property
            // else if only link element was specified, and then href points to a single result, then we will
            // perform a bind operation
            if ((linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && String.IsNullOrEmpty(link.Uri.OriginalString)) || 
                linkContent == LinkContent.EmptyInlineElementSpecified)
            { 
                // update the object count when you are performing a bind operation 
                this.CheckAndIncrementObjectCount();
                if (property != null && property.Kind == ResourcePropertyKind.ResourceSetReference) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
                }
 
                // For open properties, we will assume that this is a reference property and set it to null
                this.Service.Provider.SetReference(resource, propertyName, null); 
            } 
            else if (linkContent == LinkContent.NoInlineElementSpecified && link.Uri != null && !String.IsNullOrEmpty(link.Uri.OriginalString))
            { 
                // update the object count when you are performing a bind operation
                this.CheckAndIncrementObjectCount();

                // If the link points to a reference navigation property, then update the link 
                Uri referencedUri = RequestUriProcessor.GetAbsoluteUriFromReference(link.Uri.OriginalString, this.Service.RequestParams);
                RequestDescription description = RequestUriProcessor.ProcessRequestUri(referencedUri, this.Service); 
                if (!description.IsSingleResult) 
                {
                    if (property != null && property.Kind == ResourcePropertyKind.ResourceReference) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_LinkHrefMustReferToSingleResource(propertyName));
                    }
 
                    return;
                } 
 
                // no need to check for null. For collection properties, they can never be null and that
                // check has been added below. For reference properties, if they are null, it means unbind 
                // and hence no need to check for null.
                object propertyItem = CreateObjectFromUri(null, null, description, false /*verifyETag*/, false /*checkForNull*/, false /*replaceResource*/);
                if (property != null)
                { 
                    if (property.Kind == ResourcePropertyKind.ResourceReference)
                    { 
                        this.Service.Provider.SetReference(resource, propertyName, propertyItem); 
                    }
                    else 
                    {
                        WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
                        this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem);
                    } 
                }
                else if (typeParameterValue == null) 
                { 
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingTypeParameterOnLinkElement(propertyName));
                } 
                else if (typeParameterValue == XmlConstants.AtomEntryElementName)
                {
                    this.Service.Provider.SetReference(resource, propertyName, propertyItem);
                } 
                else
                { 
                    if (typeParameterValue != XmlConstants.AtomFeedElementName) 
                    {
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterInTypeAttributeInLink(propertyName, typeParameterValue)); 
                    }

                    WebUtil.CheckResourceExists(propertyItem != null, description.LastSegmentInfo.Identifier);
                    this.Service.Provider.AddReferenceToCollection(resource, propertyName, propertyItem); 
                }
            } 
            #endregion Handle bind/unbind operation 
        }
 
        /// Gets the type attribute and resolves the type.
        /// Item from which type attribute needs to be read
        /// Expected base type for the item.
        /// Resolved type. 
        private ResourceType GetResourceType(SyndicationItem item, Type expectedType)
        { 
            Debug.Assert(item != null, "item != null"); 
            string typeName = SyndicationItemGetType(item);
            ResourceType resourceType; 

            // If the type is not specified in the payload, we assume the type to be the expected type
            if (String.IsNullOrEmpty(typeName))
            { 
                // check if the expected type takes part in inheritance
                resourceType = this.Service.Provider.GetResourceType(expectedType); 
                if (resourceType.HasDerivedTypes) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); 
                }
            }
            else
            { 
                // Otherwise, try and resolve the name specified in the payload
                resourceType = this.Service.Provider.TryResolveTypeName(typeName); 
                if (resourceType == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName)); 
                }
            }

            return resourceType; 
        }
 
        ///  
        /// Handle the contents under the link element
        ///  
        /// syndication link element
        /// parent resource which contains the link.
        /// property representing the link.
        /// type parameter value as specified in the type attribute. 
        /// name of the property that this link represents.
        /// returns whether there are child elements under link element. 
        private LinkContent HandleLinkContent( 
            SyndicationLink link,
            object parentResource, 
            ResourceProperty property,
            string typeParameterValue,
            string propertyName)
        { 
            Debug.Assert(parentResource != null, "parent resource cannot be null");
            Debug.Assert(link != null, "link != null"); 
 
            LinkContent linkContent = LinkContent.NoInlineElementSpecified;
            foreach (var e in link.ElementExtensions) 
            {
                // link can contain other elements apart from the inline elements.
                if (e.OuterNamespace != XmlConstants.DataWebMetadataNamespace ||
                    e.OuterName != XmlConstants.AtomInlineElementName) 
                {
                    continue; 
                } 

                // Deep payload cannot be specified for update 
                if (this.Update)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported);
                } 

                linkContent = LinkContent.EmptyInlineElementSpecified; 
                using (XmlReader linkReader = e.GetReader()) 
                {
                    while (linkReader.Read()) 
                    {
                        if (linkReader.NodeType == XmlNodeType.Element)
                        {
                            string elementName = linkReader.LocalName; 
                            string namespaceUri = linkReader.NamespaceURI;
                            if (namespaceUri != XmlConstants.AtomNamespace) 
                            { 
                                throw DataServiceException.CreateBadRequestError(
                                    Strings.BadRequest_InlineElementMustContainValidElement( 
                                        elementName,
                                        XmlConstants.AtomInlineElementName,
                                        XmlConstants.AtomFeedElementName,
                                        XmlConstants.AtomEntryElementName)); 
                            }
 
                            linkContent = LinkContent.InlineElementContainsData; 
                            if (elementName == XmlConstants.AtomEntryElementName)
                            { 
                                if (property != null)
                                {
                                    if (property.Kind != ResourcePropertyKind.ResourceReference)
                                    { 
                                        throw DataServiceException.CreateBadRequestError(Strings.Syndication_EntryElementForReferenceProperties(e.OuterName, propertyName));
                                    } 
                                } 
                                else if (typeParameterValue != null && typeParameterValue != elementName)
                                { 
                                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomEntryElementName));
                                }

                                // Make sure if the media type is specified. If its specified, it should better be link 
                                SyndicationItem propertyItem;
                                propertyItem = ReadSyndicationItem(this.factory.CreateSyndicationItemFormatter(), linkReader); 
 
                                SegmentInfo propertySegment = CreateSegment(property, propertyName, true /* singleResult */);
                                object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, propertyItem); 
                                this.Service.Provider.SetReference(parentResource, propertyName, propertyValue);
                            }
                            else if (elementName == XmlConstants.AtomFeedElementName)
                            { 
                                if (property != null)
                                { 
                                    if (property.Kind != ResourcePropertyKind.ResourceSetReference) 
                                    {
                                        throw DataServiceException.CreateBadRequestError(Strings.Syndication_FeedElementForCollections(e.OuterName, propertyName)); 
                                    }
                                }
                                else if (typeParameterValue != null && typeParameterValue != elementName)
                                { 
                                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeParameterSpecifiedInMimeType(typeParameterValue, XmlConstants.AtomFeedElementName));
                                } 
 
                                SyndicationFeed propertyFeed;
                                propertyFeed = ReadSyndicationFeed(this.factory.CreateSyndicationFeedFormatter(), linkReader); 

                                SegmentInfo propertySegment = CreateSegment(property, propertyName, false /* singleResult */);
                                foreach (SyndicationItem item in propertyFeed.Items)
                                { 
                                    object propertyValue = this.CreateObject(propertySegment, false /* topLevel */, item);
                                    if (propertyValue == null) 
                                    { 
                                        if (propertySegment.ProjectedProperty != null &&
                                            propertySegment.ProjectedProperty.Kind == ResourcePropertyKind.ResourceSetReference) 
                                        {
                                            throw DataServiceException.CreateBadRequestError(
                                                Strings.BadRequest_CannotSetCollectionsToNull(propertyName));
                                        } 
                                    }
 
                                    Debug.Assert( 
#if ASTORIA_OPEN_OBJECT
                                        propertySegment.TargetKind == RequestTargetKind.OpenProperty || 
#endif
                                        (propertySegment.TargetSource == RequestTargetSource.Property && propertySegment.TargetKind == RequestTargetKind.Resource && propertySegment.SingleResult == false),
                                        "must be navigation set property or open property");
 
                                    this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue);
                                } 
                            } 
                            else
                            { 
                                throw DataServiceException.CreateBadRequestError(
                                    Strings.BadRequest_InlineElementMustContainValidElement(
                                        elementName,
                                        XmlConstants.AtomInlineElementName, 
                                        XmlConstants.AtomFeedElementName,
                                        XmlConstants.AtomEntryElementName)); 
                            } 
                        }
                    } 
                }
            }

            return linkContent; 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK