/// <summary>
        /// Reads an element in the ATOM namespace in feed or source content.
        /// </summary>
        /// <param name="atomFeedMetadata">The atom feed metadata object to store metadata details in.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:*) - the ATOM element to read.
        /// Post-Condition: Any                          - the node after the ATOM element which was read.
        /// 
        /// If the the property InSourceElement is true (i.e., we're reading within source content), then the value 
        /// of the atom:id element will be stored in the feed metadata as SourceId, otherwise it will be ignored.
        /// </remarks>
        internal void ReadAtomElementAsFeedMetadata(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:* elements can be read by this method.");

            switch (this.XmlReader.LocalName)
            {
                case AtomConstants.AtomAuthorElementName:
                    this.ReadAuthorElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomCategoryElementName:
                    this.ReadCategoryElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomContributorElementName:
                    this.ReadContributorElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomGeneratorElementName:
                    this.ReadGeneratorElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomIconElementName:
                    this.ReadIconElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomIdElementName:
                    // Only store the ID as ATOM metadata if we're within an atom:source element.
                    if (this.InSourceElement)
                    {
                        this.ReadIdElementAsSourceId(atomFeedMetadata);
                    }
                    else
                    {
                        this.XmlReader.Skip();
                    }

                    break;
                case AtomConstants.AtomLinkElementName:
                    this.ReadLinkElementIntoLinksCollection(atomFeedMetadata);
                    break;
                case AtomConstants.AtomLogoElementName:
                    this.ReadLogoElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomRightsElementName:
                    this.ReadRightsElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomSubtitleElementName:
                    this.ReadSubtitleElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomTitleElementName:
                    this.ReadTitleElement(atomFeedMetadata);
                    break;
                case AtomConstants.AtomUpdatedElementName:
                    this.ReadUpdatedElement(atomFeedMetadata);
                    break;
                default:
                    // Not something we recognize, so just ignore it.
                    this.XmlReader.Skip();
                    break;
            }
        }
Exemple #2
0
        /// <summary>
        /// Reads an atom:contributor element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:contributor) - the atom:contributor element to read.
        /// Post-Condition: Any                                    - the node after the atom:contributor element which was read.
        /// </remarks>
        private void ReadContributorElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomContributorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:contributor elements can be read by this method.");

            atomFeedMetadata.AddContributor(this.ReadAtomPersonConstruct());
        }
Exemple #3
0
        /// <summary>
        /// Reads an atom:logo element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:logo) - the atom:logo element to read.
        /// Post-Condition: Any                             - the node after the atom:logo element which was read.
        /// </remarks>
        private void ReadLogoElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomLogoElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:logo elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Logo);
            atomFeedMetadata.Logo = this.ReadUriValuedElement();
        }
Exemple #4
0
        /// <summary>
        /// Reads an atom:updated element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:updated) - the atom:updated element to read.
        /// Post-Condition: Any                                - the node after the atom:updated element which was read.
        /// </remarks>
        private void ReadUpdatedElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomUpdatedElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:updated elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Updated);
            atomFeedMetadata.Updated = this.ReadAtomDateConstruct();
        }
Exemple #5
0
        /// <summary>
        /// Reads an atom:link element into the Links collection of feed metadata (i.e., links that are not special to the OData protocol).
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:link) - the atom:link element to read.
        /// Post-Condition: Any                             - the node after the atom:link element which was read.
        /// </remarks>
        private void ReadLinkElementIntoLinksCollection(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomLinkElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:link elements can be read by this method.");

            // By sending nulls to ReadAtomLinkElementInFeed(), the method will read and store values for href and rel from the wire (inside of using parameter overrides).
            AtomLinkMetadata linkMetadata = this.ReadAtomLinkElementInFeed(null, null);

            atomFeedMetadata.AddLink(linkMetadata);
        }
        /// <summary>
        /// Writes a feed link.
        /// </summary>
        /// <param name="feed">The feed that contains the link.</param>
        /// <param name="relation">Relation attribute of the link.</param>
        /// <param name="href">href attribute of the link.</param>
        /// <param name="getLinkMetadata">Function to get the AtomLinkMetadata for the feed link.</param>
        internal void WriteFeedLink(ODataFeed feed, string relation, Uri href, Func <AtomFeedMetadata, AtomLinkMetadata> getLinkMetadata)
        {
            AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>();
            AtomLinkMetadata mergedLink   = ODataAtomWriterMetadataUtils.MergeLinkMetadata(
                getLinkMetadata(feedMetadata),
                relation,
                href,
                null,     /*title*/
                null /*mediaType*/);

            this.atomFeedMetadataSerializer.WriteAtomLink(mergedLink, null);
        }
Exemple #7
0
        /// <summary>
        /// Reads an atom:id element in a source element.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:icon) - the atom:icon element to read.
        /// Post-Condition: Any                             - the node after the atom:icon element which was read.
        /// </remarks>
        private void ReadIdElementAsSourceId(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomIdElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:id elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.SourceId);
            string elementValue = this.XmlReader.ReadElementValue();

            atomFeedMetadata.SourceId = UriUtils.CreateUriAsEntryOrFeedId(elementValue, UriKind.Absolute);
        }
        public static AtomFeedMetadata Atom(this ODataFeed feed)
        {
            ExceptionUtils.CheckArgumentNotNull(feed, "feed");

            AtomFeedMetadata feedMetadata = feed.GetAnnotation<AtomFeedMetadata>();
            if (feedMetadata == null)
            {
                feedMetadata = new AtomFeedMetadata();
                feed.SetAnnotation(feedMetadata);
            }

            return feedMetadata;
        }
        public static AtomFeedMetadata Atom(this ODataFeed feed)
        {
            ExceptionUtils.CheckArgumentNotNull(feed, "feed");

            AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>();

            if (feedMetadata == null)
            {
                feedMetadata = new AtomFeedMetadata();
                feed.SetAnnotation(feedMetadata);
            }

            return(feedMetadata);
        }
        /// <summary>
        /// Reads the atom:source element in the entry content.
        /// </summary>
        /// <returns>The information in the source element as <see cref="AtomFeedMetadata"/>.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:source) - the atom:source element to read.
        /// Post-Condition: Any                               - the node after the atom:source which was read.
        /// </remarks>
        internal AtomFeedMetadata ReadAtomSourceInEntryContent()
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomSourceElementName,
                "Only atom:source element can be read by this method.");

            AtomFeedMetadata atomFeedMetadata = AtomMetadataReaderUtils.CreateNewAtomFeedMetadata();

            if (this.XmlReader.IsEmptyElement)
            {
                // Advance reader past this element.
                this.XmlReader.Read();
                return(atomFeedMetadata);
            }

            // Read the start tag of the source element.
            this.XmlReader.Read();

            while (this.XmlReader.NodeType != XmlNodeType.EndElement)
            {
                if (this.XmlReader.NodeType != XmlNodeType.Element)
                {
                    Debug.Assert(this.XmlReader.NodeType != XmlNodeType.EndElement, "EndElement should have been handled already.");

                    // Skip everything but elements, including insignificant nodes, text nodes and CDATA nodes.
                    this.XmlReader.Skip();
                    continue;
                }

                if (this.XmlReader.NamespaceEquals(this.AtomNamespace))
                {
                    // Use a feed metadata deserializer to process this element and modify atomFeedMetadata appropriately.
                    this.SourceMetadataDeserializer.ReadAtomElementAsFeedMetadata(atomFeedMetadata);
                }
                else
                {
                    // Skip all elements not in the ATOM namespace.
                    this.XmlReader.Skip();
                }
            }

            // Advance the reader past the end tag of the source element.
            this.XmlReader.Read();

            return(atomFeedMetadata);
        }
Exemple #11
0
        /// <summary>
        /// Reads an atom:generator element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:generator) - the atom:generator element to read.
        /// Post-Condition: Any                                  - the node after the atom:generator element which was read.
        /// </remarks>
        private void ReadGeneratorElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomGeneratorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:generator elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Generator);
            AtomGeneratorMetadata generatorMetadata = new AtomGeneratorMetadata();

            // Read the attributes.
            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    switch (this.XmlReader.LocalName)
                    {
                    case AtomConstants.AtomGeneratorUriAttributeName:
                        generatorMetadata.Uri = this.ProcessUriFromPayload(this.XmlReader.Value, this.XmlReader.XmlBaseUri);
                        break;

                    case AtomConstants.AtomGeneratorVersionAttributeName:
                        generatorMetadata.Version = this.XmlReader.Value;
                        break;

                    default:
                        // Ignore all other attributes.
                        break;
                    }
                }
            }

            // Read the value of the element as a string and store it as the name of the generator.
            this.XmlReader.MoveToElement();
            if (this.XmlReader.IsEmptyElement)
            {
                // Move to the next node after this one.
                this.XmlReader.Skip();
            }
            else
            {
                generatorMetadata.Name = this.XmlReader.ReadElementValue();
            }

            atomFeedMetadata.Generator = generatorMetadata;
        }
        /// <summary>
        /// Write the given feed metadata in atom format
        /// </summary>
        /// <param name="feed">The feed for which to write the meadata or null if it is the metadata of an atom:source element.</param>
        /// <param name="updatedTime">Value for the atom:updated element.</param>
        /// <param name="authorWritten">Set to true if the author element was written, false otherwise.</param>
        internal void WriteFeedMetadata(ODataFeed feed, string updatedTime, out bool authorWritten)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(!string.IsNullOrEmpty(updatedTime), "!string.IsNullOrEmpty(updatedTime)");
#if DEBUG
            DateTimeOffset tempDateTimeOffset;
            Debug.Assert(DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset), "DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset)");
#endif

            AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>();

            if (feedMetadata == null)
            {
                // create the required metadata elements with default values.

                // <atom:id>idValue</atom:id>
                Debug.Assert(feed.Id != null, "The feed Id should have been validated by now.");
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomIdElementName,
                    AtomConstants.AtomNamespace,
                    UriUtils.UriToString(feed.Id));

                // <atom:title></atom:title>
                this.WriteEmptyElement(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace);

                // <atom:updated>dateTimeOffset</atom:updated>
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomUpdatedElementName,
                    AtomConstants.AtomNamespace,
                    updatedTime);

                authorWritten = false;
            }
            else
            {
                this.atomFeedMetadataSerializer.WriteFeedMetadata(feedMetadata, feed, updatedTime, out authorWritten);
            }
        }
Exemple #13
0
        /// <summary>
        /// Reads an atom:category element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:category) - the atom:category element to read.
        /// Post-Condition: Any                                 - the node after the atom:category which was read.
        /// </remarks>
        private void ReadCategoryElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomCategoryElementName,
                "Only atom:category element can be read by this method.");

            AtomCategoryMetadata categoryMetadata = new AtomCategoryMetadata();

            // Read the attributes
            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    switch (this.XmlReader.LocalName)
                    {
                    case AtomConstants.AtomCategorySchemeAttributeName:
                        categoryMetadata.Scheme = this.XmlReader.Value;
                        break;

                    case AtomConstants.AtomCategoryTermAttributeName:
                        categoryMetadata.Term = this.XmlReader.Value;
                        break;

                    case AtomConstants.AtomCategoryLabelAttributeName:
                        categoryMetadata.Label = this.XmlReader.Value;
                        break;

                    default:
                        // Ignore all other attributes.
                        break;
                    }
                }
            }

            atomFeedMetadata.AddCategory(categoryMetadata);

            // Skip the rest of the category element.
            this.XmlReader.Skip();
        }
        /// <summary>
        /// Writes the feed element for the atom payload.
        /// </summary>
        /// <param name="expanded">Expanded properties for the result.</param>
        /// <param name="elements">Collection of entries in the feed element.</param>
        /// <param name="expectedType">ExpectedType of the elements in the collection.</param>
        /// <param name="title">Title of the feed element.</param>
        /// <param name="getRelativeUri">Callback to get the relative uri of the feed.</param>
        /// <param name="getAbsoluteUri">Callback to get the absolute uri of the feed.</param>
        /// <param name="topLevel">True if the feed is the top level feed, otherwise false for the inner expanded feed.</param>
        private void WriteFeedElements(
            IExpandedResult expanded,
            QueryResultInfo elements,
            ResourceType expectedType,
            string title,
            Func<Uri> getRelativeUri,
            Func<Uri> getAbsoluteUri,
            bool topLevel)
        {
            Debug.Assert(elements != null, "elements != null");
            Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType");
            Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)");

            ODataFeed feed = new ODataFeed();
            feed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName });

            // Write the other elements for the feed
            this.PayloadMetadataPropertyManager.SetId(feed, () => getAbsoluteUri());

#pragma warning disable 618
            if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
            {
                // Create the atom feed metadata and set the self link and title value.
                AtomFeedMetadata feedMetadata = new AtomFeedMetadata();
                feed.SetAnnotation(feedMetadata);
                feedMetadata.Title = new AtomTextConstruct { Text = title };
                feedMetadata.SelfLink = new AtomLinkMetadata { Href = getRelativeUri(), Title = title };
            }

            // support for $count
            // in ATOM we write it at the beginning (we always have)
            //   in JSON for backward compatiblity reasons we write it at the end, so we must not fill it here.
            if (topLevel && this.RequestDescription.CountOption == RequestQueryCountOption.CountQuery)
            {
                feed.Count = this.RequestDescription.CountValue;
            }

            var feedArgs = elements.GetDataServiceODataWriterFeedArgs(feed, this.Service.OperationContext);
            this.dataServicesODataWriter.WriteStart(feedArgs);
            object lastObject = null;
            IExpandedResult lastExpandedSkipToken = null;
            while (elements.HasMoved)
            {
                object o = elements.Current;
                IExpandedResult skipToken = this.GetSkipToken(expanded);

                if (o != null)
                {
                    IExpandedResult expandedO = o as IExpandedResult;
                    if (expandedO != null)
                    {
                        expanded = expandedO;
                        o = GetExpandedElement(expanded);
                        skipToken = this.GetSkipToken(expanded);
                    }

                    this.WriteEntry(expanded, o, true /*resourceInstanceInFeed*/, expectedType);
                }

                elements.MoveNext();
                lastObject = o;
                lastExpandedSkipToken = skipToken;
            }

            // After looping through the objects in the sequence, decide if we need to write the next
            // page link and if yes, write it by invoking the delegate
            if (this.NeedNextPageLink(elements))
            {
                this.PayloadMetadataPropertyManager.SetNextPageLink(
                    feed,
                    this.AbsoluteServiceUri,
                    this.GetNextLinkUri(lastObject, lastExpandedSkipToken, getAbsoluteUri()));
            }

            this.dataServicesODataWriter.WriteEnd(feedArgs);
#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteFeed(feed);
#endif
        }
        public void EntryMetadataWriterTest()
        {
            const string testPublished = "2010-10-20T20:10:00Z";
            AtomTextConstruct testRights = new AtomTextConstruct { Text = "Copyright Data Fx team." };
            AtomTextConstruct testSummary = new AtomTextConstruct { Text = "Test summary." };
            AtomTextConstruct testTitle = new AtomTextConstruct { Text = "Test title" };
            const string testUpdated = "2010-11-01T00:04:00Z";
            const string testIcon = "http://odata.org/icon";
            const string testSourceId = "http://odata.org/id/random";
            const string testLogo = "http://odata.org/logo";
            AtomTextConstruct testSubtitle = new AtomTextConstruct { Text = "Test subtitle." };

            const string testAuthorName = "Test Author 1";
            const string testAuthorEmail = "*****@*****.**";
            const string testAuthorUri = "http://odata.org/authors/1";

            var testAuthors = new AtomPersonMetadata[]
                {
                    new AtomPersonMetadata()
                    {
                        Email = testAuthorEmail,
                        Name = testAuthorName,
                        Uri = new Uri(testAuthorUri)
                    }
                };

            var testAuthors2 = new AtomPersonMetadata[0];

            const string testCategoryTerm = "Test category 1 term";
            const string testCategoryLabel = "Test category 1 label";
            const string testCategoryScheme = "http://odata.org/categories/1";

            var testCategories = new AtomCategoryMetadata[]
                {
                    new AtomCategoryMetadata()
                    {
                        Term = testCategoryTerm,
                        Label = testCategoryLabel,
                        Scheme = testCategoryScheme
                    }
                };

            const string testContributorName = "Test Contributor 1";
            const string testContributorEmail = "*****@*****.**";
            const string testContributorUri = "http://odata.org/contributors/1";

            var testContributors = new AtomPersonMetadata[]
                {
                    new AtomPersonMetadata()
                    {
                        Email = testContributorEmail,
                        Name = testContributorName,
                        Uri = new Uri(testContributorUri)
                    }
                };

            const string testGeneratorName = "Test generator";
            const string testGeneratorUri = "http://odata.org/generator";
            const string testGeneratorVersion = "3.0";

            var testGenerator = new AtomGeneratorMetadata()
                {
                    Name = testGeneratorName,
                    Uri = new Uri(testGeneratorUri),
                    Version = testGeneratorVersion
                };

            const string testLinkRelation = "http://odata.org/links/1";
            const string testLinkTitle = "Test link 1";
            const string testLinkHref = "http://odata.org/links/1";
            const string testLinkHrefLang = "de-AT";
            int? testLinkLength = 999;
            const string testLinkMediaType = "image/png";

            var testLinks = new AtomLinkMetadata[] 
                {
                    new AtomLinkMetadata()
                    {
                        Relation = testLinkRelation,
                        Title = testLinkTitle,
                        Href = new Uri(testLinkHref),
                        HrefLang = testLinkHrefLang,
                        Length = testLinkLength,
                        MediaType = testLinkMediaType
                    }
                };

            AtomFeedMetadata testSource = new AtomFeedMetadata()
            {
                Authors = testAuthors,
                Categories = testCategories,
                Contributors = testContributors,
                Generator = testGenerator,
                Icon = new Uri(testIcon),
                SourceId = new Uri(testSourceId),
                Links = testLinks,
                Logo = new Uri(testLogo),
                Rights = testRights,
                Subtitle = testSubtitle,
                Title = testTitle,
                Updated = DateTimeOffset.Parse(testUpdated)
            };

            Func<string, Func<XElement, XElement>> fragmentExtractor = (localName) => (e) => e.Element(TestAtomConstants.AtomXNamespace + localName);

            // TODO, ckerer: specify an Id via metadata if the entry does not specify one; we first have to decide what rules
            //               we want to apply to merging of metadata and ODataLib OM data.
            var testCases = new[] {
                new { // specify an author via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name>" + testAuthorName + @"</name>",
                        @"  <uri>" + testAuthorUri + @"</uri>",
                        @"  <email>" + testAuthorEmail + @"</email>",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify an empty author array via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors2),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name />",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify no authors via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = null),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"$(Indent)<name />",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify a category via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Categories = testCategories),
                    Xml = @"<category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" xmlns=""" + TestAtomConstants.AtomNamespace + @""" />",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomCategoryElementName)
                },
                new { // specify a contributor via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Contributors = testContributors),
                    Xml = string.Join(
                        "$(NL)",
                        @"<contributor xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name>" + testContributorName + @"</name>",
                        @"  <uri>" + testContributorUri + @"</uri>",
                        @"  <email>" + testContributorEmail + @"</email>",
                        @"</contributor>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomContributorElementName)
                },
                new { // specify a link via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Links = testLinks),
                    Xml = @"<link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @"""  xmlns=""" + TestAtomConstants.AtomNamespace + @"""/>",
                    Extractor = new Func<XElement, XElement>(
                            (e) => e.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                    .Where(l => l.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value != TestAtomConstants.AtomSelfRelationAttributeValue)
                                    .Single())
                },
                new { // specify a published date via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Published = DateTimeOffset.Parse(testPublished)),
                    Xml = @"<published xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testPublished + @"</published>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomPublishedElementName)
                },
                new { // specify rights via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Rights = testRights),
                    Xml = @"<rights type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testRights.Text + @"</rights>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomRightsElementName)
                },
                new { // specify a source via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = testSource),
                    Xml = string.Join(
                        "$(NL)",
                        @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <id>" + testSourceId + "</id>",
                        @"  <title type=""text"">" + testTitle.Text + @"</title>",
                        @"  <subtitle type=""text"">" + testSubtitle.Text + @"</subtitle>",
                        @"  <updated>" + testUpdated + @"</updated>",
                        @"  <link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @"""/>",
                        @"  <category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" />",
                        @"  <logo>" + testLogo + @"</logo>",
                        @"  <rights type=""text"">" + testRights.Text + @"</rights>",
                        @"  <contributor>",
                        @"    <name>" + testContributorName + @"</name>",
                        @"    <uri>" + testContributorUri + @"</uri>",
                        @"    <email>" + testContributorEmail + @"</email>",
                        @"  </contributor>",
                        @"  <generator uri=""" + testGeneratorUri + @""" version=""" +testGeneratorVersion + @""">" + testGeneratorName + @"</generator>",
                        @"  <icon>" + testIcon + @"</icon>",
                        @"  <author>",
                        @"    <name>" + testAuthorName + @"</name>",
                        @"    <uri>" + testAuthorUri + @"</uri>",
                        @"    <email>" + testAuthorEmail + @"</email>",
                        @"  </author>",
                        @"</source>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomSourceElementName)
                },
                new { // specify default feed metadata as source
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = new AtomFeedMetadata()),
                    Xml = string.Join(
                        "$(NL)",
                        @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <id />",
                        @"  <title />",
                        @"  <updated />",
                        @"</source>"),
                    Extractor = new Func<XElement, XElement>(result => {
                        var source = fragmentExtractor(TestAtomConstants.AtomSourceElementName)(result);
                        // Remove the value of updates as it can't be reliably predicted
                        source.Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomUpdatedElementName).Nodes().Remove();
                        return source;
                    })
                },
                new { // specify a summary via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Summary = testSummary),
                    Xml = @"<summary type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testSummary.Text + @"</summary>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomSummaryElementName)
                },
                new { // specify a title via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Title = testTitle),
                    Xml = @"<title type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle.Text + @"</title>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomTitleElementName)
                },
                new { // specify an updated date via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Updated = DateTimeOffset.Parse(testUpdated)),
                    Xml = @"<updated xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testUpdated + @"</updated>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomUpdatedElementName)
                },
            };

            // Convert test cases to test descriptions
            IEnumerable<Func<ODataEntry>> entryCreators = new Func<ODataEntry>[]
                {
                    () => ObjectModelUtils.CreateDefaultEntry(),
                    () => ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(),
                };
            var testDescriptors = testCases.SelectMany(testCase =>
                entryCreators.Select(entryCreator =>
                {
                    ODataEntry entry = entryCreator();
                    AtomEntryMetadata metadata = entry.Atom();
                    this.Assert.IsNotNull(metadata, "Expected default entry metadata on a default entry.");
                    testCase.CustomizeMetadata(metadata);
                    return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration =>
                        new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.Xml, FragmentExtractor = testCase.Extractor });
                }));

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors.PayloadCases(WriterPayloads.EntryPayloads),
                this.WriterTestConfigurationProvider.AtomFormatConfigurations,
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger);
                });
        }
            /// <summary>
            /// Converts the Object Model representation of Atom metadata for feeds into appropriate PayloadElement annotations
            /// </summary>
            /// <param name="feedMetadata">the Atom feed metadata, in Object Model representation, to convert</param>
            /// <param name="feed">the EntitySetInstance to annotate</param>
            private static void ConvertAtomFeedMetadata(AtomFeedMetadata feedMetadata, EntitySetInstance feed)
            {
                ExceptionUtilities.CheckArgumentNotNull(feedMetadata, "feedMetadata");
                ExceptionUtilities.CheckArgumentNotNull(feed, "feed");

                foreach (var author in feedMetadata.Authors)
                {
                    feed.AtomAuthor(author.Name, author.Uri == null ? null : author.Uri.OriginalString, author.Email);
                }

                foreach (var category in feedMetadata.Categories)
                {
                    feed.AtomCategory(category.Term, category.Scheme, category.Label);
                }

                foreach (var contributor in feedMetadata.Contributors)
                {
                    feed.AtomContributor(contributor.Name, contributor.Uri == null ? null : contributor.Uri.OriginalString, contributor.Email);
                }

                if (feedMetadata.Generator != null)
                {
                    feed.AtomGenerator(feedMetadata.Generator.Name, feedMetadata.Generator.Uri == null ? null : feedMetadata.Generator.Uri.OriginalString, feedMetadata.Generator.Version);
                }

                if (feedMetadata.Icon != null)
                {
                    feed.AtomIcon(feedMetadata.Icon.OriginalString);
                }

                foreach (var link in feedMetadata.Links)
                {
                    string linkLength = link.Length.HasValue ? link.Length.Value.ToString() : null;
                    feed.AtomLink(link.Href == null ? null : link.Href.OriginalString, link.Relation, link.MediaType, link.HrefLang, link.Title, linkLength);
                }

                if (feedMetadata.Logo != null)
                {
                    feed.AtomLogo(feedMetadata.Logo.OriginalString);
                }

                if (feedMetadata.Rights != null)
                {
                    feed.AtomRights(feedMetadata.Rights.Text, ToString(feedMetadata.Rights.Kind));
                }

                if (feedMetadata.SelfLink != null)
                {
                    AtomLinkMetadata selfLink = feedMetadata.SelfLink;
                    ExceptionUtilities.Assert(selfLink.Relation == TestAtomConstants.AtomSelfRelationAttributeValue, "The self link ATOM metadata must have the rel set to 'self'.");
                    string selfLinkLength = selfLink.Length.HasValue ? selfLink.Length.Value.ToString() : null;
                    feed.AtomSelfLink(selfLink.Href == null ? null : selfLink.Href.OriginalString, selfLink.MediaType, selfLink.HrefLang, selfLink.Title, selfLinkLength);
                }

                if (feedMetadata.NextPageLink != null)
                {
                    AtomLinkMetadata nextPageLink = feedMetadata.NextPageLink;
                    ExceptionUtilities.Assert(nextPageLink.Relation == TestAtomConstants.AtomNextRelationAttributeValue, "The next page link ATOM metadata must have the rel set to 'next'.");
                    string nextPageLinkLength = nextPageLink.Length.HasValue ? nextPageLink.Length.Value.ToString() : null;
                    feed.AtomNextPageLink(nextPageLink.Href == null ? null : nextPageLink.Href.OriginalString, nextPageLink.MediaType, nextPageLink.HrefLang, nextPageLink.Title, nextPageLinkLength);
                }

                if (feedMetadata.SourceId != null)
                {
                    // This should only occur when the metadata comes from the source element of an entry
                    // and we are annotating a temporary feed instance
                    feed.AtomId(UriUtils.UriToString(feedMetadata.SourceId));
                }

                if (feedMetadata.Subtitle != null)
                {
                    feed.AtomSubtitle(feedMetadata.Subtitle.Text, ToString(feedMetadata.Subtitle.Kind));
                }

                if (feedMetadata.Title != null)
                {
                    feed.AtomTitle(feedMetadata.Title.Text, ToString(feedMetadata.Title.Kind));
                }

                if (feedMetadata.Updated.HasValue)
                {
                    feed.AtomUpdated(ToString(feedMetadata.Updated));
                }
            }
        /// <summary>
        /// Reads an atom:category element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:category) - the atom:category element to read.
        /// Post-Condition: Any                                 - the node after the atom:category which was read.
        /// </remarks>
        private void ReadCategoryElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomCategoryElementName,
                "Only atom:category element can be read by this method.");

            AtomCategoryMetadata categoryMetadata = new AtomCategoryMetadata();

            // Read the attributes
            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    switch (this.XmlReader.LocalName)
                    {
                        case AtomConstants.AtomCategorySchemeAttributeName:
                            categoryMetadata.Scheme = this.XmlReader.Value;
                            break;
                        case AtomConstants.AtomCategoryTermAttributeName:
                            categoryMetadata.Term = this.XmlReader.Value;
                            break;
                        case AtomConstants.AtomCategoryLabelAttributeName:
                            categoryMetadata.Label = this.XmlReader.Value;
                            break;
                        default:
                            // Ignore all other attributes.
                            break;
                    }
                }
            }

            atomFeedMetadata.AddCategory(categoryMetadata);

            // Skip the rest of the category element.
            this.XmlReader.Skip();
        }
        /// <summary>
        /// Reads an atom:contributor element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:contributor) - the atom:contributor element to read.
        /// Post-Condition: Any                                    - the node after the atom:contributor element which was read.
        /// </remarks>
        private void ReadContributorElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomContributorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:contributor elements can be read by this method.");

            atomFeedMetadata.AddContributor(this.ReadAtomPersonConstruct());
        }
        /// <summary>
        /// Visits an ATOM feed metadata.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to visit.</param>
        protected virtual void VisitAtomFeedMetadata(AtomFeedMetadata atomFeedMetadata)
        {
            IEnumerable<AtomPersonMetadata> authors = atomFeedMetadata.Authors;
            if (authors != null)
            {
                foreach (AtomPersonMetadata author in authors)
                {
                    this.VisitAtomMetadata(author);
                }
            }

            IEnumerable<AtomCategoryMetadata> categories = atomFeedMetadata.Categories;
            if (categories != null)
            {
                foreach (AtomCategoryMetadata category in categories)
                {
                    this.VisitAtomMetadata(category);
                }
            }

            IEnumerable<AtomPersonMetadata> contributors = atomFeedMetadata.Contributors;
            if (contributors != null)
            {
                foreach (AtomPersonMetadata contributor in contributors)
                {
                    this.VisitAtomMetadata(contributor);
                }
            }

            this.VisitAtomMetadata(atomFeedMetadata.Generator);

            IEnumerable<AtomLinkMetadata> links = atomFeedMetadata.Links;
            if (links != null)
            {
                foreach (AtomLinkMetadata link in links)
                {
                    this.VisitAtomMetadata(link);
                }
            }

            this.VisitAtomMetadata(atomFeedMetadata.Rights);
            this.VisitAtomMetadata(atomFeedMetadata.SelfLink);
            this.VisitAtomMetadata(atomFeedMetadata.Subtitle);
            this.VisitAtomMetadata(atomFeedMetadata.Title);
        }
 /// <summary>
 /// Creates a an ODataFeed instance with default values for 'Id' and 'Updated'
 /// that can be used and modified in tests.
 /// </summary>
 /// <returns>The created ODataFeed instance.</returns>
 public static ODataFeed CreateDefaultFeedWithAtomMetadata()
 {
     ODataFeed feed = new ODataFeed()
     {
         Id = DefaultFeedId,
         SerializationInfo = MySerializationInfo
     };
     AtomFeedMetadata metadata = new AtomFeedMetadata()
     {
         Updated = DateTimeOffset.Parse(DefaultFeedUpdated)
     };
     feed.SetAnnotation<AtomFeedMetadata>(metadata);
     return feed;
 }
        /// <summary>
        /// Reads an atom:generator element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:generator) - the atom:generator element to read.
        /// Post-Condition: Any                                  - the node after the atom:generator element which was read.
        /// </remarks>
        private void ReadGeneratorElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomGeneratorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:generator elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Generator);
            AtomGeneratorMetadata generatorMetadata = new AtomGeneratorMetadata();

            // Read the attributes.
            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    switch (this.XmlReader.LocalName)
                    {
                        case AtomConstants.AtomGeneratorUriAttributeName:
                            generatorMetadata.Uri = this.ProcessUriFromPayload(this.XmlReader.Value, this.XmlReader.XmlBaseUri);
                            break;
                        case AtomConstants.AtomGeneratorVersionAttributeName:
                            generatorMetadata.Version = this.XmlReader.Value;
                            break;
                        default:
                            // Ignore all other attributes.
                            break;
                    }
                }
            }

            // Read the value of the element as a string and store it as the name of the generator.
            this.XmlReader.MoveToElement();
            if (this.XmlReader.IsEmptyElement)
            {
                // Move to the next node after this one.
                this.XmlReader.Skip();
            }
            else
            {
                generatorMetadata.Name = this.XmlReader.ReadElementValue();
            }

            atomFeedMetadata.Generator = generatorMetadata;
        }
        /// <summary>
        /// Reads an atom:id element in a source element.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:icon) - the atom:icon element to read.
        /// Post-Condition: Any                             - the node after the atom:icon element which was read.
        /// </remarks>
        private void ReadIdElementAsSourceId(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomIdElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:id elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.SourceId);
            string elementValue = this.XmlReader.ReadElementValue();

            atomFeedMetadata.SourceId = UriUtils.CreateUriAsEntryOrFeedId(elementValue, UriKind.Absolute);
        }
        /// <summary>
        /// Reads an atom:updated element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:updated) - the atom:updated element to read.
        /// Post-Condition: Any                                - the node after the atom:updated element which was read.
        /// </remarks>
        private void ReadUpdatedElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomUpdatedElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:updated elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Updated);
            atomFeedMetadata.Updated = this.ReadAtomDateConstruct();
        }
        /// <summary>
        /// Reads an atom:logo element in a feed.
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:logo) - the atom:logo element to read.
        /// Post-Condition: Any                             - the node after the atom:logo element which was read.
        /// </remarks>
        private void ReadLogoElement(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomLogoElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:logo elements can be read by this method.");

            this.VerifyNotPreviouslyDefined(atomFeedMetadata.Logo);
            atomFeedMetadata.Logo = this.ReadUriValuedElement();
        }
        /// <summary>
        /// Reads an atom:link element into the Links collection of feed metadata (i.e., links that are not special to the OData protocol).
        /// </summary>
        /// <param name="atomFeedMetadata">The feed metadata to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:link) - the atom:link element to read.
        /// Post-Condition: Any                             - the node after the atom:link element which was read.
        /// </remarks>
        private void ReadLinkElementIntoLinksCollection(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomLinkElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:link elements can be read by this method.");

            // By sending nulls to ReadAtomLinkElementInFeed(), the method will read and store values for href and rel from the wire (inside of using parameter overrides).
            AtomLinkMetadata linkMetadata = this.ReadAtomLinkElementInFeed(null, null);
            atomFeedMetadata.AddLink(linkMetadata);
        }
Exemple #26
0
        internal void WriteEntryMetadata(AtomEntryMetadata entryMetadata, string updatedTime)
        {
            Debug.Assert(!string.IsNullOrEmpty(updatedTime), "!string.IsNullOrEmpty(updatedTime)");
#if DEBUG
            DateTimeOffset tempDateTimeOffset;
            Debug.Assert(DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset), "DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset)");
#endif

            // TODO: implement the rule around authors (an entry has to have an author directly or in the <entry:source> unless the feed has an author).
            //               currently we make all entries have an author.
            if (entryMetadata == null)
            {
                // write all required metadata elements with default content

                // <atom:title></atom:title>
                this.WriteEmptyElement(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace);

                // <atom:updated>dateTimeOffset</atom:updated>
                // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload.
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomUpdatedElementName,
                    AtomConstants.AtomNamespace,
                    updatedTime);

                this.WriteEmptyAuthor();
            }
            else
            {
                // <atom:title>text</atom:title>
                // NOTE: writes an empty element even if no title was specified since the title is required
                this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, entryMetadata.Title);

                AtomTextConstruct summary = entryMetadata.Summary;
                if (summary != null)
                {
                    // <atom:summary>text</atom:summary>
                    this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSummaryElementName, AtomConstants.AtomNamespace, summary);
                }

                string published = entryMetadata.Published.HasValue
                        ? ODataAtomConvert.ToAtomString(entryMetadata.Published.Value)
                        : null;
                if (published != null)
                {
                    // <atom:published>dateTimeOffset</atom:published>
                    this.WriteElementWithTextContent(
                        AtomConstants.AtomNamespacePrefix,
                        AtomConstants.AtomPublishedElementName,
                        AtomConstants.AtomNamespace,
                        published);
                }

                // <atom:updated>date</atom:updated>
                string updated = entryMetadata.Updated.HasValue
                        ? ODataAtomConvert.ToAtomString(entryMetadata.Updated.Value)
                        : null;

                // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload.
                updated = updated ?? updatedTime;
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomUpdatedElementName,
                    AtomConstants.AtomNamespace,
                    updated);

                bool wroteAuthor = false;
                IEnumerable <AtomPersonMetadata> authors = entryMetadata.Authors;
                if (authors != null)
                {
                    foreach (AtomPersonMetadata author in authors)
                    {
                        if (author == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_AuthorMetadataMustNotContainNull);
                        }

                        // <atom:author>author data</atom:author>
                        this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace);
                        this.WritePersonMetadata(author);
                        this.XmlWriter.WriteEndElement();
                        wroteAuthor = true;
                    }
                }

                if (!wroteAuthor)
                {
                    // write empty authors since they are required
                    this.WriteEmptyAuthor();
                }

                IEnumerable <AtomPersonMetadata> contributors = entryMetadata.Contributors;
                if (contributors != null)
                {
                    foreach (AtomPersonMetadata contributor in contributors)
                    {
                        if (contributor == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_ContributorMetadataMustNotContainNull);
                        }

                        // <atom:contributor>contributor data</atom:contributor>
                        this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace);
                        this.WritePersonMetadata(contributor);
                        this.XmlWriter.WriteEndElement();
                    }
                }

                IEnumerable <AtomLinkMetadata> links = entryMetadata.Links;
                if (links != null)
                {
                    foreach (AtomLinkMetadata link in links)
                    {
                        if (link == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_LinkMetadataMustNotContainNull);
                        }

                        // <atom:link>...</atom:link>
                        this.WriteAtomLink(link, null /*edit-link-etag*/);
                    }
                }

                IEnumerable <AtomCategoryMetadata> categories = entryMetadata.Categories;
                if (categories != null)
                {
                    foreach (AtomCategoryMetadata category in categories)
                    {
                        if (category == null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_CategoryMetadataMustNotContainNull);
                        }

                        // <atom:category term="..." scheme="..." label="..."></atom:category>
                        this.WriteCategory(category);
                    }
                }

                if (entryMetadata.Rights != null)
                {
                    // <atom:rights>rights</atom:rights>
                    this.WriteTextConstruct(
                        AtomConstants.AtomNamespacePrefix,
                        AtomConstants.AtomRightsElementName,
                        AtomConstants.AtomNamespace,
                        entryMetadata.Rights);
                }

                AtomFeedMetadata source = entryMetadata.Source;
                if (source != null)
                {
                    // <atom:source>
                    this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSourceElementName, AtomConstants.AtomNamespace);

                    bool authorWritten;
                    this.SourceMetadataSerializer.WriteFeedMetadata(source, null /* feed */, updatedTime, out authorWritten);

                    // </atom:source>
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
        internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten)
        {
            Debug.Assert(feedMetadata != null, "Feed metadata must not be null!");

            // <atom:id>text</atom:id>
            // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself;
            // if used in the context of an <atom:source> element it is stored in metadata
            Uri id = feed == null ? feedMetadata.SourceId : feed.Id;
            this.WriteElementWithTextContent(
                AtomConstants.AtomNamespacePrefix,
                AtomConstants.AtomIdElementName,
                AtomConstants.AtomNamespace,
                id == null ? null : UriUtils.UriToString(id));

            // <atom:title>text</atom:title>
            // NOTE: write an empty element if no title is specified since the element is required
            this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title);

            if (feedMetadata.Subtitle != null)
            {
                // <atom:subtitle>text</atom:subtitle>
                this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle);
            }

            // <atom:updated>date</atom:updated>
            // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload.
            string updated = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime;
            this.WriteElementWithTextContent(
                AtomConstants.AtomNamespacePrefix,
                AtomConstants.AtomUpdatedElementName,
                AtomConstants.AtomNamespace,
                updated);

            AtomLinkMetadata selfLinkMetadata = feedMetadata.SelfLink;
            if (selfLinkMetadata != null)
            {
                AtomLinkMetadata mergedSelfLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(
                    selfLinkMetadata,
                    AtomConstants.AtomSelfRelationAttributeValue,
                    null /* href */,
                    null /* title */,
                    null /* media type */);
                this.WriteAtomLink(mergedSelfLinkMetadata, null /*etag*/);
            }

            IEnumerable<AtomLinkMetadata> links = feedMetadata.Links;
            if (links != null)
            {
                foreach (AtomLinkMetadata link in links)
                {
                    // DeltaLink is written from ODataFeed, so it shouldn't be written again from AtomFeedMetadata.
                    if (link.Relation != AtomConstants.AtomDeltaRelationAttributeValue)
                    {
                        // <atom:link>...</atom:link>
                        this.WriteAtomLink(link, null /* etag */);
                    }
                }
            }

            IEnumerable<AtomCategoryMetadata> categories = feedMetadata.Categories;
            if (categories != null)
            {
                foreach (AtomCategoryMetadata category in categories)
                {
                    // <atom:category term="..." scheme="..." label="..."></atom:category>
                    this.WriteCategory(category);
                }
            }

            Uri logo = feedMetadata.Logo;
            if (logo != null)
            {
                // <atom:logo>Uri</atom:logo>
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomLogoElementName,
                    AtomConstants.AtomNamespace,
                    this.UriToUrlAttributeValue(logo));
            }

            if (feedMetadata.Rights != null)
            {
                // <atom:rights>rights</atom:rights>
                this.WriteTextConstruct(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomRightsElementName,
                    AtomConstants.AtomNamespace,
                    feedMetadata.Rights);
            }

            IEnumerable<AtomPersonMetadata> contributors = feedMetadata.Contributors;
            if (contributors != null)
            {
                foreach (AtomPersonMetadata contributor in contributors)
                {
                    // <atom:contributor>contributor data</atom:contributor>
                    this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace);
                    this.WritePersonMetadata(contributor);
                    this.XmlWriter.WriteEndElement();
                }
            }

            AtomGeneratorMetadata generator = feedMetadata.Generator;
            if (generator != null)
            {
                // <atom:generator uri="..." version="...">name</atom:generator>
                this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace);
                if (generator.Uri != null)
                {
                    this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, this.UriToUrlAttributeValue(generator.Uri));
                }

                if (!string.IsNullOrEmpty(generator.Version))
                {
                    this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version);
                }

                ODataAtomWriterUtils.WriteString(this.XmlWriter, generator.Name);
                this.XmlWriter.WriteEndElement();
            }

            Uri icon = feedMetadata.Icon;
            if (icon != null)
            {
                // <atom:icon>Uri</atom:icon>
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomIconElementName,
                    AtomConstants.AtomNamespace,
                    this.UriToUrlAttributeValue(icon));
            }

            IEnumerable<AtomPersonMetadata> authors = feedMetadata.Authors;
            authorWritten = false;
            if (authors != null)
            {
                foreach (AtomPersonMetadata author in authors)
                {
                    // <atom:author>author data</atom:author>
                    authorWritten = true;
                    this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace);
                    this.WritePersonMetadata(author);
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
Exemple #28
0
        /// <summary>
        /// Reads an element in the ATOM namespace in feed or source content.
        /// </summary>
        /// <param name="atomFeedMetadata">The atom feed metadata object to store metadata details in.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:*) - the ATOM element to read.
        /// Post-Condition: Any                          - the node after the ATOM element which was read.
        ///
        /// If the the property InSourceElement is true (i.e., we're reading within source content), then the value
        /// of the atom:id element will be stored in the feed metadata as SourceId, otherwise it will be ignored.
        /// </remarks>
        internal void ReadAtomElementAsFeedMetadata(AtomFeedMetadata atomFeedMetadata)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:* elements can be read by this method.");

            switch (this.XmlReader.LocalName)
            {
            case AtomConstants.AtomAuthorElementName:
                this.ReadAuthorElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomCategoryElementName:
                this.ReadCategoryElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomContributorElementName:
                this.ReadContributorElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomGeneratorElementName:
                this.ReadGeneratorElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomIconElementName:
                this.ReadIconElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomIdElementName:
                // Only store the ID as ATOM metadata if we're within an atom:source element.
                if (this.InSourceElement)
                {
                    this.ReadIdElementAsSourceId(atomFeedMetadata);
                }
                else
                {
                    this.XmlReader.Skip();
                }

                break;

            case AtomConstants.AtomLinkElementName:
                this.ReadLinkElementIntoLinksCollection(atomFeedMetadata);
                break;

            case AtomConstants.AtomLogoElementName:
                this.ReadLogoElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomRightsElementName:
                this.ReadRightsElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomSubtitleElementName:
                this.ReadSubtitleElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomTitleElementName:
                this.ReadTitleElement(atomFeedMetadata);
                break;

            case AtomConstants.AtomUpdatedElementName:
                this.ReadUpdatedElement(atomFeedMetadata);
                break;

            default:
                // Not something we recognize, so just ignore it.
                this.XmlReader.Skip();
                break;
            }
        }
        internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten)
        {
            Debug.Assert(feedMetadata != null, "Feed metadata must not be null!");

            // <atom:id>text</atom:id>
            // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself;
            // if used in the context of an <atom:source> element it is stored in metadata
            Uri id = feed == null ? feedMetadata.SourceId : feed.Id;

            this.WriteElementWithTextContent(
                AtomConstants.AtomNamespacePrefix,
                AtomConstants.AtomIdElementName,
                AtomConstants.AtomNamespace,
                id == null ? null : UriUtils.UriToString(id));

            // <atom:title>text</atom:title>
            // NOTE: write an empty element if no title is specified since the element is required
            this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title);

            if (feedMetadata.Subtitle != null)
            {
                // <atom:subtitle>text</atom:subtitle>
                this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle);
            }

            // <atom:updated>date</atom:updated>
            // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload.
            string updated = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime;

            this.WriteElementWithTextContent(
                AtomConstants.AtomNamespacePrefix,
                AtomConstants.AtomUpdatedElementName,
                AtomConstants.AtomNamespace,
                updated);

            AtomLinkMetadata selfLinkMetadata = feedMetadata.SelfLink;

            if (selfLinkMetadata != null)
            {
                AtomLinkMetadata mergedSelfLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(
                    selfLinkMetadata,
                    AtomConstants.AtomSelfRelationAttributeValue,
                    null /* href */,
                    null /* title */,
                    null /* media type */);
                this.WriteAtomLink(mergedSelfLinkMetadata, null /*etag*/);
            }

            IEnumerable <AtomLinkMetadata> links = feedMetadata.Links;

            if (links != null)
            {
                foreach (AtomLinkMetadata link in links)
                {
                    // DeltaLink is written from ODataFeed, so it shouldn't be written again from AtomFeedMetadata.
                    if (link.Relation != AtomConstants.AtomDeltaRelationAttributeValue)
                    {
                        // <atom:link>...</atom:link>
                        this.WriteAtomLink(link, null /* etag */);
                    }
                }
            }

            IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories;

            if (categories != null)
            {
                foreach (AtomCategoryMetadata category in categories)
                {
                    // <atom:category term="..." scheme="..." label="..."></atom:category>
                    this.WriteCategory(category);
                }
            }

            Uri logo = feedMetadata.Logo;

            if (logo != null)
            {
                // <atom:logo>Uri</atom:logo>
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomLogoElementName,
                    AtomConstants.AtomNamespace,
                    this.UriToUrlAttributeValue(logo));
            }

            if (feedMetadata.Rights != null)
            {
                // <atom:rights>rights</atom:rights>
                this.WriteTextConstruct(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomRightsElementName,
                    AtomConstants.AtomNamespace,
                    feedMetadata.Rights);
            }

            IEnumerable <AtomPersonMetadata> contributors = feedMetadata.Contributors;

            if (contributors != null)
            {
                foreach (AtomPersonMetadata contributor in contributors)
                {
                    // <atom:contributor>contributor data</atom:contributor>
                    this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace);
                    this.WritePersonMetadata(contributor);
                    this.XmlWriter.WriteEndElement();
                }
            }

            AtomGeneratorMetadata generator = feedMetadata.Generator;

            if (generator != null)
            {
                // <atom:generator uri="..." version="...">name</atom:generator>
                this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace);
                if (generator.Uri != null)
                {
                    this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, this.UriToUrlAttributeValue(generator.Uri));
                }

                if (!string.IsNullOrEmpty(generator.Version))
                {
                    this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version);
                }

                ODataAtomWriterUtils.WriteString(this.XmlWriter, generator.Name);
                this.XmlWriter.WriteEndElement();
            }

            Uri icon = feedMetadata.Icon;

            if (icon != null)
            {
                // <atom:icon>Uri</atom:icon>
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomIconElementName,
                    AtomConstants.AtomNamespace,
                    this.UriToUrlAttributeValue(icon));
            }

            IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors;

            authorWritten = false;
            if (authors != null)
            {
                foreach (AtomPersonMetadata author in authors)
                {
                    // <atom:author>author data</atom:author>
                    authorWritten = true;
                    this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace);
                    this.WritePersonMetadata(author);
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
        private static AtomFeedMetadata CreateFeedMetadata(IEnumerable<XmlTreeAnnotation> children, ODataFeed feed)
        {
            AtomFeedMetadata metadata = null;

            foreach (XmlTreeAnnotation epmTree in children)
            {
                if (epmTree.NamespaceName == TestAtomConstants.AtomNamespace)
                {
                    if (metadata == null)
                    {
                        metadata = new AtomFeedMetadata();
                    }

                    string localName = epmTree.LocalName;
                    if (localName == TestAtomConstants.AtomAuthorElementName)
                    {
                        AtomPersonMetadata author = CreateAuthorMetadata(epmTree.Children);

                        List<AtomPersonMetadata> authors;
                        if (metadata.Authors == null)
                        {
                            authors = new List<AtomPersonMetadata>();
                            metadata.Authors = authors;
                        }
                        else
                        {
                            authors = (List<AtomPersonMetadata>)metadata.Authors;
                        }
                        authors.Add(author);
                    }
                    else if (localName == TestAtomConstants.AtomCategoryElementName)
                    {
                        AtomCategoryMetadata category = CreateCategoryMetadata(epmTree.Children);

                        List<AtomCategoryMetadata> categories;
                        if (metadata.Categories == null)
                        {
                            categories = new List<AtomCategoryMetadata>();
                            metadata.Categories = categories;
                        }
                        else
                        {
                            categories = (List<AtomCategoryMetadata>)metadata.Categories;
                        }
                        categories.Add(category);
                    }
                    else if (localName == TestAtomConstants.AtomContributorElementName)
                    {
                        AtomPersonMetadata contributor = CreateAuthorMetadata(epmTree.Children);

                        List<AtomPersonMetadata> contributors;
                        if (metadata.Contributors == null)
                        {
                            contributors = new List<AtomPersonMetadata>();
                            metadata.Contributors = contributors;
                        }
                        else
                        {
                            contributors = (List<AtomPersonMetadata>)metadata.Contributors;
                        }
                        contributors.Add(contributor);
                    }
                    else if (localName == TestAtomConstants.AtomGeneratorElementName)
                    {
                        metadata.Generator = CreateGeneratorMetadata(epmTree);
                    }
                    else if (localName == TestAtomConstants.AtomIconElementName)
                    {
                        metadata.Icon = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomIdElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        Uri id = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue, UriKind.Absolute);
                        if (feed == null)
                        {
                            // we are creating the metadata for an entry's 'source' metadata;
                            // we don't have a feed to store the Id on so it has to go into metadata
                            metadata.SourceId = id;
                        }
                        else
                        {
                            feed.Id = id;
                        }
                    }
                    else if (localName == TestAtomConstants.AtomLinkElementName)
                    {
                        AtomLinkMetadata link = CreateLinkMetadata(epmTree.Children);

                        List<AtomLinkMetadata> links;
                        if (metadata.Links == null)
                        {
                            links = new List<AtomLinkMetadata>();
                            metadata.Links = links;
                        }
                        else
                        {
                            links = (List<AtomLinkMetadata>)metadata.Contributors;
                        }
                        links.Add(link);
                    }
                    else if (localName == TestAtomConstants.AtomLogoElementName)
                    {
                        metadata.Logo = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomRightsElementName)
                    {
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Rights = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomSubtitleElementName)
                    {
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Subtitle = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomTitleElementName)
                    {
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Title = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomUpdatedElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Updated = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue);
                    }
                    else
                    {
                        throw new NotSupportedException("Unsupported atom metadata found!");
                    }
                }
            }

            return metadata;
        }
        public void PayloadOrderTest()
        {
            ODataFeedAndEntrySerializationInfo info = new ODataFeedAndEntrySerializationInfo()
            {
                NavigationSourceEntityTypeName = "Null",
                NavigationSourceName = "MySet",
                ExpectedTypeName = "Null"
            };

            AtomFeedMetadata defaultAtomFeedMetadata = new AtomFeedMetadata() { 
                Authors = new[] { new AtomPersonMetadata { Name = "Bart" } }, 
                Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime };

            IEnumerable<PayloadOrderTestCase> testCases = new[]
            {
                new PayloadOrderTestCase
                {
                    DebugDescription = "Just ID",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info }
                        .WithAnnotation(defaultAtomFeedMetadata),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>",
                        "  <author>",
                        "    <name>Bart</name>",
                        "  </author>{0}",
                        "</feed>"),
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "Just ID, and another ID at the end, the one from the starts is used",
                    Feed = new ODataFeed() { SerializationInfo = info }
                        .WithAnnotation(new WriteFeedCallbacksAnnotation
                            {
                                BeforeWriteStartCallback = (feed) => { feed.Id = new Uri("http://MyFeedId"); },
                                BeforeWriteEndCallback = (feed) => { feed.Id = new Uri("http://AnotherFeedId"); }
                            })
                        .WithAnnotation(defaultAtomFeedMetadata),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>",
                        "  <author>",
                        "    <name>Bart</name>",
                        "  </author>{0}",
                        "</feed>"),
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "ID and no author metadata and no entities, the default author should be written.",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info }
                        .WithAnnotation(new AtomFeedMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>{0}",
                        "  <author>",
                        "    <name />",
                        "  </author>",
                        "</feed>"),
                    NoEntitiesOnly = true
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "ID and no author metadata but with entities, no author is written.",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info }
                        .WithAnnotation(new AtomFeedMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>{0}",
                        "</feed>"),
                    WithEntitiesOnly = true
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "ID, count at the end - the count is ignored",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info }
                        .WithAnnotation(new WriteFeedCallbacksAnnotation
                            {
                                BeforeWriteStartCallback = (feed) => { feed.Count = null; },
                                BeforeWriteEndCallback = (feed) => { feed.Count = 42; }
                            })
                        .WithAnnotation(defaultAtomFeedMetadata),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>",
                        "  <author>",
                        "    <name>Bart</name>",
                        "  </author>{0}",
                        "</feed>"),
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "ID, count and next link",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), Count = 42, NextPageLink = new Uri("http://odata.org/nextlink"), SerializationInfo = info  }
                        .WithAnnotation(defaultAtomFeedMetadata),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <count xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">42</count>",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>",
                        "  <author>",
                        "    <name>Bart</name>",
                        "  </author>{0}",
                        "  <link rel=\"next\" href=\"http://odata.org/nextlink\" />",
                        "</feed>"),
                },
                new PayloadOrderTestCase
                {
                    DebugDescription = "ID, count and next link and some ATOM metadata, metadata goes before entries but author is the last of them.",
                    Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), Count = 42, NextPageLink = new Uri("http://odata.org/nextlink"), SerializationInfo = info }
                        .WithAnnotation(new AtomFeedMetadata() { 
                            Authors = new[] { new AtomPersonMetadata { Name = "Bart" } }, 
                            Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime,
                            Logo = new Uri("http://odata.org/logo") }),
                    Xml = string.Join(
                        "$(NL)",
                        "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                        "  <count xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">42</count>",
                        "  <id>http://MyFeedId</id>",
                        "  <title />",
                        "  <updated>2010-10-12T17:13:00Z</updated>",
                        "  <logo>http://odata.org/logo</logo>",
                        "  <author>",
                        "    <name>Bart</name>",
                        "  </author>{0}",
                        "  <link rel=\"next\" href=\"http://odata.org/nextlink\" />",
                        "</feed>"),
                },
            };

            IEnumerable<PayloadWriterTestDescriptor<ODataItem>> testDescriptors = testCases.Where(testCase => !testCase.WithEntitiesOnly).Select(testCase =>
                new PayloadWriterTestDescriptor<ODataItem>(
                    this.Settings,
                    new ODataItem[] { testCase.Feed },
                    tc => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        Xml = string.Format(CultureInfo.InvariantCulture, testCase.Xml, string.Empty),
                        FragmentExtractor = (element) => element
                    })
                {
                    DebugDescription = testCase.DebugDescription,
                });

            testDescriptors = testDescriptors.Concat(testCases.Where(testCase => !testCase.NoEntitiesOnly).Select(testCase =>
                new PayloadWriterTestDescriptor<ODataItem>(
                    this.Settings,
                    new ODataItem[] { 
                        testCase.Feed, 
                        new ODataEntry { }
                            .WithAnnotation(new AtomEntryMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }),
                    },
                    tc => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        Xml = string.Format(CultureInfo.InvariantCulture, testCase.Xml,
                            string.Join(
                                "$(NL)",
                                "<entry xmlns=\"" + TestAtomConstants.AtomNamespace + "\">",
                                "  <id />",
                                "  <title />",
                                "  <updated>2010-10-12T17:13:00Z</updated>",
                                "  <author>",
                                "    <name />",
                                "  </author>",
                                "  <content type=\"application/xml\" />",
                                "</entry>")),
                        FragmentExtractor = (element) => element
                    })
                {
                    DebugDescription = testCase.DebugDescription + "- with entry",
                }));

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors,
                this.WriterTestConfigurationProvider.AtomFormatConfigurationsWithIndent
                    .Where(tc => !tc.IsRequest),
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger);
                });
        }
 /// <summary>
 /// Visits an ATOM feed metadata.
 /// </summary>
 /// <param name="atomFeedMetadata">The feed metadata to visit.</param>
 protected override void VisitAtomFeedMetadata(AtomFeedMetadata atomFeedMetadata)
 {
     this.ValidateUri(atomFeedMetadata.Icon);
     this.ValidateUri(atomFeedMetadata.Logo);
     base.VisitAtomFeedMetadata(atomFeedMetadata);
 }
        public void BaseUriErrorTest()
        {
            Uri baseUri = new Uri("http://odata.org");
            Uri testUri = new Uri("http://odata.org/relative");
            IEnumerable<Func<Uri, BaseUriErrorTestCase>> testCaseFuncs = new Func<Uri, BaseUriErrorTestCase>[] 
            {
                relativeUri => new BaseUriErrorTestCase
                {   // next page link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        feed.NextPageLink = relativeUri;
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // entry read link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        entry.ReadLink = relativeUri;
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // entry edit link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        entry.EditLink = relativeUri;
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // media resource (default stream) read link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataStreamReferenceValue mediaResource = new ODataStreamReferenceValue();
                        mediaResource.ContentType = "image/jpg";
                        mediaResource.ReadLink = relativeUri;
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        entry.MediaResource = mediaResource;
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // media resource (default stream) edit link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataStreamReferenceValue mediaResource = new ODataStreamReferenceValue();
                        mediaResource.ContentType = "image/jpg";    // required
                        mediaResource.ReadLink = testUri;           // required
                        mediaResource.EditLink = relativeUri;
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        entry.MediaResource = mediaResource;
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // link Url
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataNavigationLink link = ObjectModelUtils.CreateDefaultCollectionLink();
                        link.Url = relativeUri;

                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        return new ODataItem[] { entry, link };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // association link Url
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataNavigationLink link = ObjectModelUtils.CreateDefaultSingletonLink();
                        link.AssociationLinkUrl = relativeUri;

                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        return new ODataItem[] { entry, link };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // named stream read link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataStreamReferenceValue namedStream = new ODataStreamReferenceValue()
                        {
                            ContentType = "image/jpg",
                            ReadLink = relativeUri,
                        };
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        ODataProperty property = new ODataProperty()
                        {
                            Name = "NamedStream",
                            Value = namedStream
                        };

                        entry.Properties = new[] { property };
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // named stream edit link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataStreamReferenceValue namedStream = new ODataStreamReferenceValue()
                        {
                            ContentType = "image/jpg",
                            ReadLink = testUri,
                            EditLink = relativeUri
                        };
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        ODataProperty property = new ODataProperty()
                        {
                            Name = "NamedStream",
                            Value = namedStream
                        };

                        entry.Properties = new[] { property };
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom, ODataFormat.Json }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed generator Uri
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Generator = new AtomGeneratorMetadata()
                            {
                                Uri = relativeUri
                            }
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed logo
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Logo = relativeUri
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed icon
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Icon = relativeUri
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed author
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Authors = new []
                            {
                                new AtomPersonMetadata()
                                {
                                    Uri = relativeUri
                                }
                            }
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed contributor
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Contributors = new []
                            {
                                new AtomPersonMetadata()
                                {
                                    Uri = relativeUri
                                }
                            }
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: feed link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataFeed feed = ObjectModelUtils.CreateDefaultFeed();
                        AtomFeedMetadata metadata = new AtomFeedMetadata()
                        {
                            Links = new []
                            {
                                new AtomLinkMetadata()
                                {
                                    Href = relativeUri
                                }
                            }
                        };
                        feed.SetAnnotation<AtomFeedMetadata>(metadata);
                        return new [] { feed };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: entry author
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        AtomEntryMetadata metadata = new AtomEntryMetadata()
                        {
                            Authors = new []
                            {
                                new AtomPersonMetadata()
                                {
                                    Uri = relativeUri
                                }
                            }
                        };
                        entry.SetAnnotation<AtomEntryMetadata>(metadata);
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: entry contributor
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        AtomEntryMetadata metadata = new AtomEntryMetadata()
                        {
                            Contributors = new []
                            {
                                new AtomPersonMetadata()
                                {
                                    Uri = relativeUri
                                }
                            }
                        };
                        entry.SetAnnotation<AtomEntryMetadata>(metadata);
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
                relativeUri => new BaseUriErrorTestCase
                {   // Atom metadata: entry link
                    ItemFunc = new Func<IEnumerable<ODataItem>>(() => 
                    {
                        ODataEntry entry = ObjectModelUtils.CreateDefaultEntry();
                        AtomEntryMetadata metadata = new AtomEntryMetadata()
                        {
                            Links = new []
                            {
                                new AtomLinkMetadata()
                                {
                                    Href = relativeUri
                                }
                            }
                        };
                        entry.SetAnnotation<AtomEntryMetadata>(metadata);
                        return new [] { entry };
                    }),
                    Formats = new [] { ODataFormat.Atom }
                },
            };

            // ToDo: Fix places where we've lost JsonVerbose coverage to add JsonLight
            Uri testRelativeUri = baseUri.MakeRelativeUri(testUri);
            Uri invalidRelativeUri = new Uri("../invalid/relative/uri", UriKind.Relative);
            this.CombinatorialEngineProvider.RunCombinations(
                testCaseFuncs,
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurations.Where(c => c.IsRequest == false && c.Format == ODataFormat.Atom),
                new Uri[] { testRelativeUri, invalidRelativeUri },
                new bool[] { false, true },
                (testCaseFunc, testConfiguration, uri, implementUrlResolver) =>
                {
                    var testCase = testCaseFunc(uri);
                    var testDescriptor = new
                    {
                        Descriptor = new PayloadWriterTestDescriptor<ODataItem>(
                            this.Settings,
                            testCase.ItemFunc(),
                            testConfig => new WriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                            {
                                ExpectedException2 = ODataExpectedExceptions.ODataException("ODataWriter_RelativeUriUsedWithoutBaseUriSpecified", uri.OriginalString)
                            }),
                        Formats = testCase.Formats
                    };

                    if (testDescriptor.Formats.Contains(testConfiguration.Format))
                    {
                        PayloadWriterTestDescriptor<ODataItem> payloadTestDescriptor = testDescriptor.Descriptor;
                        TestUrlResolver urlResolver = null;
                        if (implementUrlResolver)
                        {
                            payloadTestDescriptor = new PayloadWriterTestDescriptor<ODataItem>(payloadTestDescriptor);
                            urlResolver = new TestUrlResolver();
                            payloadTestDescriptor.UrlResolver = urlResolver;
                        }

                        testConfiguration = testConfiguration.Clone();
                        testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                        TestWriterUtils.WriteAndVerifyODataPayload(payloadTestDescriptor, testConfiguration, this.Assert, this.Logger);

                        if (implementUrlResolver)
                        {
                            this.Assert.AreEqual(1, urlResolver.Calls.Where(call => call.Value.OriginalString == uri.OriginalString).Count(), "The resolver should be called exactly once for each URL.");
                        }
                    }
                });
        }