/// <summary>
        /// Writes the edit link element for an entry.
        /// </summary>
        /// <param name="editLink">The edit link URL.</param>
        /// <param name="entryMetadata">The ATOM entry metatadata for the current entry.</param>
        internal void WriteEntryEditLink(Uri editLink, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(editLink != null, "editLink != null");

            // we allow additional link metadata to specify the title, type, hreflang or length of the link
            AtomLinkMetadata editLinkMetadata = entryMetadata == null ? null : entryMetadata.EditLink;

            // <link rel="edit" href="LinkHRef" .../>
            this.WriteReadOrEditLink(editLink, editLinkMetadata, AtomConstants.AtomEditRelationAttributeValue);
        }
 /// <summary>
 /// Writes the type name category element for the entry.
 /// </summary>
 /// <param name="typeName">The type name to write.</param>
 /// <param name="entryMetadata">The entry metadata if available.</param>
 internal void WriteEntryTypeName(string typeName, AtomEntryMetadata entryMetadata)
 {
     if (typeName != null)
     {
         AtomCategoryMetadata mergedCategoryMetadata = ODataAtomWriterMetadataUtils.MergeCategoryMetadata(
             entryMetadata == null ? null : entryMetadata.CategoryWithTypeName,
             typeName,
             AtomConstants.ODataSchemeNamespace);
         this.atomEntryMetadataSerializer.WriteCategory(mergedCategoryMetadata);
     }
 }
 /// <summary>
 /// Writes the type name category element for the entry.
 /// </summary>
 /// <param name="typeName">The type name to write.</param>
 /// <param name="entryMetadata">The entry metadata if available.</param>
 internal void WriteEntryTypeName(string typeName, AtomEntryMetadata entryMetadata)
 {
     if (typeName != null)
     {
         AtomCategoryMetadata mergedCategoryMetadata = ODataAtomWriterMetadataUtils.MergeCategoryMetadata(
             entryMetadata == null ? null : entryMetadata.CategoryWithTypeName,
             typeName,
             AtomConstants.ODataSchemeNamespace);
         this.atomEntryMetadataSerializer.WriteCategory(mergedCategoryMetadata);
     }
 }
Esempio n. 4
0
        public static AtomEntryMetadata Atom(this ODataEntry entry)
        {
            ExceptionUtils.CheckArgumentNotNull(entry, "entry");

            AtomEntryMetadata entryMetadata = entry.GetAnnotation<AtomEntryMetadata>();
            if (entryMetadata == null)
            {
                entryMetadata = new AtomEntryMetadata();
                entry.SetAnnotation(entryMetadata);
            }

            return entryMetadata;
        }
Esempio n. 5
0
        public static AtomEntryMetadata Atom(this ODataEntry entry)
        {
            ExceptionUtils.CheckArgumentNotNull(entry, "entry");

            AtomEntryMetadata entryMetadata = entry.GetAnnotation <AtomEntryMetadata>();

            if (entryMetadata == null)
            {
                entryMetadata = new AtomEntryMetadata();
                entry.SetAnnotation(entryMetadata);
            }

            return(entryMetadata);
        }
        /// <summary>
        /// Writes the edit link element for an entry.
        /// </summary>
        /// <param name="editLink">The edit link URL.</param>
        /// <param name="entryMetadata">The ATOM entry metatadata for the current entry.</param>
        internal void WriteEntryEditLink(Uri editLink, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(editLink != null, "editLink != null");

            // we allow additional link metadata to specify the title, type, hreflang or length of the link
            AtomLinkMetadata editLinkMetadata = entryMetadata == null ? null : entryMetadata.EditLink;

            // <link rel="edit" href="LinkHRef" .../>
            this.WriteReadOrEditLink(editLink, editLinkMetadata, AtomConstants.AtomEditRelationAttributeValue);
        }
        private static void AddEntryMetadata(EntityInstance payloadElement, ODataEntry entry)
        {
            AtomEntryMetadata metadata = null;

            foreach (XmlTreeAnnotation epmTree in payloadElement.Annotations.OfType<XmlTreeAnnotation>())
            {
                if (epmTree.NamespaceName == TestAtomConstants.AtomNamespace)
                {
                    if (metadata == null)
                    {
                        metadata = new AtomEntryMetadata();
                    }

                    string localName = epmTree.LocalName;
                    if (localName == TestAtomConstants.AtomAuthorElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        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)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        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)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        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.AtomIdElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        entry.Id = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomLinkElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomLinkMetadata link = CreateLinkMetadata(epmTree.Children);

                        List<AtomLinkMetadata> links;
                        if (metadata.Links == null)
                        {
                            links = new List<AtomLinkMetadata>();
                            metadata.Links = links;
                        }
                        else
                        {
                            links = (List<AtomLinkMetadata>)metadata.Links;
                        }
                        links.Add(link);
                    }
                    else if (localName == TestAtomConstants.AtomPublishedElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Published = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomRightsElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Rights = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomSourceElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Source = CreateFeedMetadata(epmTree.Children, null);
                    }
                    else if (localName == TestAtomConstants.AtomSummaryElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Summary = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomTitleElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        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 '" + localName + "' found for entry!");
                    }
                }
            }

            // Fix up metadata for baselining
            metadata = metadata.Fixup();

            if (metadata != null)
            {
                entry.SetAnnotation<AtomEntryMetadata>(metadata);
            }
        }
            /// <summary>
            /// Converts the Object Model representation of Atom metadata for entries into appropriate PayloadElement annotations
            /// </summary>
            /// <param name="entryMetadata">the Atom entry metadata, in Object Model representation, to convert</param>
            /// <param name="entry">the EntityInstance to annotate</param>
            private static void ConvertAtomEntryMetadata(AtomEntryMetadata entryMetadata, EntityInstance entry)
            {
                ExceptionUtilities.CheckArgumentNotNull(entryMetadata, "entryMetadata");
                ExceptionUtilities.CheckArgumentNotNull(entry, "entry");

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

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

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

                if (entryMetadata.EditLink != null)
                {
                    AtomLinkMetadata editLink = entryMetadata.EditLink;
                    entry.AtomEditLink(editLink.Href == null ? null : editLink.Href.OriginalString, editLink.MediaType, editLink.HrefLang, editLink.Title, ToString(editLink.Length));
                }

                foreach (var link in entryMetadata.Links)
                {
                    entry.AtomLink(link.Href == null ? null : link.Href.OriginalString, link.Relation, link.MediaType, link.HrefLang, link.Title, ToString(link.Length));
                }

                if (entryMetadata.Published.HasValue)
                {
                    entry.AtomPublished(ToString(entryMetadata.Published));
                }

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

                if (entryMetadata.SelfLink != null)
                {
                    AtomLinkMetadata selfLink = entryMetadata.SelfLink;
                    entry.AtomSelfLink(selfLink.Href == null ? null : selfLink.Href.OriginalString, selfLink.MediaType, selfLink.HrefLang, selfLink.Title, ToString(selfLink.Length));
                }

                if (entryMetadata.CategoryWithTypeName != null)
                {
                    AtomCategoryMetadata categoryWithTypeName = entryMetadata.CategoryWithTypeName;
                    entry.AtomCategoryWithTypeName(categoryWithTypeName.Term, categoryWithTypeName.Label);
                }

                if (entryMetadata.Source != null)
                {
                    EntitySetInstance tempSourceFeed = new EntitySetInstance();
                    ConvertAtomFeedMetadata(entryMetadata.Source, tempSourceFeed);
                    entry.AtomSource(tempSourceFeed);
                }

                if (entryMetadata.Summary != null)
                {
                    entry.AtomSummary(entryMetadata.Summary.Text, ToString(entryMetadata.Summary.Kind));
                }

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

                if (entryMetadata.Updated.HasValue)
                {
                    entry.AtomUpdated(ToString(entryMetadata.Updated));
                }
            }
        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, ckerer: 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();
                }
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Start writing an entry.
        /// </summary>
        /// <param name="entry">The entry to write.</param>
        protected override void StartEntry(ODataEntry entry)
        {
            this.CheckAndWriteParentNavigationLinkStartForInlineElement();
            Debug.Assert(
                this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            if (entry == null)
            {
                Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation.");

                // this is a null expanded single entry and it is null, an empty <m:inline /> will be written.
                return;
            }

            // <entry>
            this.atomOutputContext.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomEntryElementName, AtomConstants.AtomNamespace);

            if (this.IsTopLevel)
            {
                this.atomEntryAndFeedSerializer.WriteBaseUriAndDefaultNamespaceAttributes();

                // Write metadata:context
                this.atomEntryAndFeedSerializer.TryWriteEntryContextUri(this.CurrentEntryScope.GetOrCreateTypeContext(this.atomOutputContext.Model, this.atomOutputContext.WritingResponse));
            }

            string etag = entry.ETag;

            if (etag != null)
            {
                // TODO, ckerer: if this is a top-level entry also put the ETag into the headers.
                ODataAtomWriterUtils.WriteETag(this.atomOutputContext.XmlWriter, etag);
            }

            AtomEntryScope    currentEntryScope = this.CurrentEntryScope;
            AtomEntryMetadata entryMetadata     = entry.Atom();

            // Write the id if it's available here.
            // If it's not available here we will try to write it at the end of the entry again.
            Uri  entryId     = entry.Id;
            bool isTransient = entry.IsTransient;

            if (entryId != null)
            {
                this.atomEntryAndFeedSerializer.WriteEntryId(entryId, isTransient);
                currentEntryScope.SetWrittenElement(AtomElement.Id);
            }

            // <category term="type" scheme="odatascheme"/>
            // If no type information is provided, don't include the category element for type at all
            // NOTE: the validation of the type name is done by the core writer.
            string typeName = this.atomOutputContext.TypeNameOracle.GetEntryTypeNameForWriting(entry);

            this.atomEntryAndFeedSerializer.WriteEntryTypeName(typeName, entryMetadata);

            // Write the edit link if it's available here.
            // If it's not available here we will try to write it at the end of the entry again.
            Uri editLink = entry.EditLink;

            if (editLink != null)
            {
                this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata);
                currentEntryScope.SetWrittenElement(AtomElement.EditLink);
            }

            // Write the self link if it's available here.
            // If it's not available here we will try to write it at the end of the entry again.
            // If readlink is identical to editlink, don't write readlink.
            Uri readLink = entry.ReadLink;

            if (readLink != null)
            {
                if (readLink != editLink)
                {
                    this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata);
                }

                currentEntryScope.SetWrittenElement(AtomElement.ReadLink);
            }

            this.WriteInstanceAnnotations(entry.InstanceAnnotations, currentEntryScope.InstanceAnnotationWriteTracker);
        }
Esempio n. 11
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();
                }
            }
        }
Esempio n. 12
0
        /// <summary>
        /// Creates an ODataEntry instance with the default values for 'Id', 'ReadLink' and 'Updated'
        /// that can be used and modified in tests.
        /// </summary>
        /// <param name="typeName">The optional type name for the default entry.</param>
        /// <param name="model">The product model to generate the type in (if not null).</param>
        /// <returns>The newly created ODataEntry instance.</returns>
        public static ODataEntry CreateDefaultEntryWithAtomMetadata(string entitySetName = null, string typeName = null, EdmModel model = null)
        {
            if (model != null && typeName != null)
            {
                EdmEntityType entityType = new EdmEntityType(DefaultNamespaceName, typeName);
                entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(isNullable: false)));
                model.AddElement(entityType);

                typeName = entityType.FullName();

                EdmEntityContainer container = new EdmEntityContainer(DefaultNamespaceName, "DefaultContainer");
                model.AddElement(container);

                if (entitySetName != null)
                {
                    container.AddEntitySet(entitySetName, entityType);
                }
            }

            ODataEntry entry = new ODataEntry()
            {
                Id = DefaultEntryId,
                ReadLink = DefaultEntryReadLink,
                TypeName = typeName,
                SerializationInfo = MySerializationInfo
            };
            AtomEntryMetadata metadata = new AtomEntryMetadata()
            {
                Updated = DateTimeOffset.Parse(DefaultEntryUpdated)
            };
            entry.SetAnnotation<AtomEntryMetadata>(metadata);
            return entry;
        }
Esempio n. 13
0
        /// <summary>Write the entry element.</summary>
        /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param>
        /// <param name="element">Element representing the entry element.</param>
        /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param>
        /// <param name="expectedType">Expected type of the entry element.</param>
        private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType)
        {
            Debug.Assert(element != null, "element != null");
            Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType");
            this.IncrementSegmentResultCount();

            ODataEntry entry = new ODataEntry();
            if (!resourceInstanceInFeed)
            {
                entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName });
            }

            string title = expectedType.Name;
#pragma warning disable 618
            if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
            {
                AtomEntryMetadata entryAtom = new AtomEntryMetadata();
                entryAtom.EditLink = new AtomLinkMetadata { Title = title };
                entry.SetAnnotation(entryAtom);
            }

            ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
            if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                // making sure that the actual resource type is an entity type
                throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName));
            }

            EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType);

            // populate the media resource, if the entity is a MLE.
            entry.MediaResource = this.GetMediaResource(entityToSerialize, title);

            // Write the type name
            this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName);

            // Write Id element
            this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity);

            // Write "edit" link
            this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink);

            // Write the etag property, if the type has etag properties
            this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType));
            
            IEnumerable<ProjectionNode> projectionNodes = this.GetProjections();
            if (projectionNodes != null)
            {
                // Filter the projection nodes for the actual type of the entity
                // The projection node might refer to the property in a derived type. If the TargetResourceType of
                // the projection node is not a super type, then we do not want to serialize this property.
                projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType));

                // Because we are going to enumerate through these multiple times, create a list.
                projectionNodes = projectionNodes.ToList();

                // And add the annotation to tell ODataLib which properties to write into content (the projections)
                entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName)));
            }

            // Populate the advertised actions
            IEnumerable<ODataAction> actions;
            if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions))
            {
                foreach (ODataAction action in actions)
                {
                    entry.AddAction(action);
                }
            }

            // Populate all the normal properties
            entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes);

            // And start the entry
            var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext);
            this.dataServicesODataWriter.WriteStart(args);

            // Now write all the navigation properties
            this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes);

            // And write the end of the entry
            this.dataServicesODataWriter.WriteEnd(args);

#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteItem(target, element);
#endif
        }
        /// <summary>
        /// Visits an ATOM entry metadata.
        /// </summary>
        /// <param name="atomEntryMetadata">The entry metadata to visit.</param>
        protected virtual void VisitAtomEntryMetadata(AtomEntryMetadata atomEntryMetadata)
        {
            IEnumerable<AtomPersonMetadata> authors = atomEntryMetadata.Authors;
            if (authors != null)
            {
                foreach (AtomPersonMetadata author in authors)
                {
                    this.VisitAtomMetadata(author);
                }
            }

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

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

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

            this.VisitAtomMetadata(atomEntryMetadata.Rights);
            this.VisitAtomMetadata(atomEntryMetadata.EditLink);
            this.VisitAtomMetadata(atomEntryMetadata.SelfLink);
            this.VisitAtomMetadata(atomEntryMetadata.CategoryWithTypeName);
            this.VisitAtomMetadata(atomEntryMetadata.Source);
            this.VisitAtomMetadata(atomEntryMetadata.Summary);
            this.VisitAtomMetadata(atomEntryMetadata.Title);
        }
 /// <summary>
 /// Write the ATOM metadata for an entry
 /// </summary>
 /// <param name="entryMetadata">The entry metadata to write.</param>
 /// <param name="updatedTime">Value for the atom:updated element.</param>
 internal void WriteEntryMetadata(AtomEntryMetadata entryMetadata, string updatedTime)
 {
     this.atomEntryMetadataSerializer.WriteEntryMetadata(entryMetadata, updatedTime);
 }
        /// <summary>
        /// Reads an element in ATOM namespace in the content of the entry element.
        /// </summary>
        /// <param name="entryState">The reader entry state for the entry being read.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:*) - the ATOM element to read.
        /// Post-Condition: Any                          - the node after the ATOM element which was read.
        /// </remarks>
        internal void ReadAtomElementInEntryContent(IODataAtomReaderEntryState entryState)
        {
            Debug.Assert(entryState != null, "entryState != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:* elements can be read by this method.");

            if (this.ReadAtomMetadata)
            {
                switch (this.XmlReader.LocalName)
                {
                case AtomConstants.AtomAuthorElementName:
                    this.ReadAuthorElement(entryState);
                    return;

                case AtomConstants.AtomContributorElementName:
                    this.ReadContributorElement(entryState);
                    return;

                case AtomConstants.AtomUpdatedElementName:
                {
                    AtomEntryMetadata entryMetadata = entryState.AtomEntryMetadata;
                    if (this.ShouldReadSingletonElement(entryMetadata.Updated.HasValue))
                    {
                        entryMetadata.Updated = this.ReadAtomDateConstruct();
                        return;
                    }
                }

                break;

                case AtomConstants.AtomPublishedElementName:
                {
                    AtomEntryMetadata entryMetadata = entryState.AtomEntryMetadata;
                    if (this.ShouldReadSingletonElement(entryMetadata.Published.HasValue))
                    {
                        entryMetadata.Published = this.ReadAtomDateConstruct();
                        return;
                    }
                }

                break;

                case AtomConstants.AtomRightsElementName:
                    if (this.ShouldReadSingletonElement(entryState.AtomEntryMetadata.Rights != null))
                    {
                        entryState.AtomEntryMetadata.Rights = this.ReadAtomTextConstruct();
                        return;
                    }

                    break;

                case AtomConstants.AtomSourceElementName:
                    if (this.ShouldReadSingletonElement(entryState.AtomEntryMetadata.Source != null))
                    {
                        entryState.AtomEntryMetadata.Source = this.ReadAtomSourceInEntryContent();
                        return;
                    }

                    break;

                case AtomConstants.AtomSummaryElementName:
                    if (this.ShouldReadSingletonElement(entryState.AtomEntryMetadata.Summary != null))
                    {
                        entryState.AtomEntryMetadata.Summary = this.ReadAtomTextConstruct();
                        return;
                    }

                    break;

                case AtomConstants.AtomTitleElementName:
                    if (this.ShouldReadSingletonElement(entryState.AtomEntryMetadata.Title != null))
                    {
                        entryState.AtomEntryMetadata.Title = this.ReadAtomTextConstruct();
                        return;
                    }

                    break;

                default:
                    break;
                }
            }

            // Skip everything we didn't read.
            this.XmlReader.Skip();
        }
 /// <summary>
 /// Write the ATOM metadata for an entry
 /// </summary>
 /// <param name="entryMetadata">The entry metadata to write.</param>
 /// <param name="updatedTime">Value for the atom:updated element.</param>
 internal void WriteEntryMetadata(AtomEntryMetadata entryMetadata, string updatedTime)
 {
     this.atomEntryMetadataSerializer.WriteEntryMetadata(entryMetadata, updatedTime);
 }
Esempio n. 18
0
        protected override void EndEntry(ODataEntry entry)
        {
            Debug.Assert(
                this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            if (entry == null)
            {
                Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation.");

                // this is a null expanded single entry and it is null, an empty <m:inline /> will be written.
                this.CheckAndWriteParentNavigationLinkEndForInlineElement();
                return;
            }

            IEdmEntityType entryType = this.EntryEntityType;

            // Initialize the property value cache and cache the entry properties.
            EntryPropertiesValueCache propertyValueCache = new EntryPropertiesValueCache(entry);

            // Get the projected properties annotation
            AtomEntryScope currentEntryScope = this.CurrentEntryScope;
            ProjectedPropertiesAnnotation projectedProperties = GetProjectedPropertiesAnnotation(currentEntryScope);
            AtomEntryMetadata             entryMetadata       = entry.Atom();

            if (!currentEntryScope.IsElementWritten(AtomElement.Id))
            {
                // NOTE: We write even null id, in that case we generate an empty atom:id element.
                bool isTransient = entry.IsTransient;
                this.atomEntryAndFeedSerializer.WriteEntryId(entry.Id, isTransient);
            }

            Uri editLink = entry.EditLink;

            if (editLink != null && !currentEntryScope.IsElementWritten(AtomElement.EditLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata);
            }

            Uri readLink = entry.ReadLink;

            if (readLink != null && readLink != editLink && !currentEntryScope.IsElementWritten(AtomElement.ReadLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata);
            }

            // write entry metadata
            this.atomEntryAndFeedSerializer.WriteEntryMetadata(entryMetadata, this.updatedTime);

            // stream properties
            IEnumerable <ODataProperty> streamProperties = propertyValueCache.EntryStreamProperties;

            if (streamProperties != null)
            {
                foreach (ODataProperty streamProperty in streamProperties)
                {
                    this.atomEntryAndFeedSerializer.WriteStreamProperty(
                        streamProperty,
                        entryType,
                        this.DuplicatePropertyNamesChecker,
                        projectedProperties);
                }
            }

            // actions
            IEnumerable <ODataAction> actions = entry.Actions;

            if (actions != null)
            {
                foreach (ODataAction action in actions)
                {
                    ValidationUtils.ValidateOperationNotNull(action, true);
                    this.atomEntryAndFeedSerializer.WriteOperation(action);
                }
            }

            // functions
            IEnumerable <ODataFunction> functions = entry.Functions;

            if (functions != null)
            {
                foreach (ODataFunction function in functions)
                {
                    ValidationUtils.ValidateOperationNotNull(function, false);
                    this.atomEntryAndFeedSerializer.WriteOperation(function);
                }
            }

            // write the content
            this.WriteEntryContent(
                entry,
                entryType,
                propertyValueCache,
                projectedProperties);

            this.WriteInstanceAnnotations(entry.InstanceAnnotations, currentEntryScope.InstanceAnnotationWriteTracker);

            // </entry>
            this.atomOutputContext.XmlWriter.WriteEndElement();

            this.CheckAndWriteParentNavigationLinkEndForInlineElement();
        }
Esempio n. 19
0
        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.");
                        }
                    }
                });
        }