/// <summary>
        /// Writes an Xml element with the specified primitive value as content.
        /// </summary>
        /// <param name="prefix">The prefix for the element's namespace.</param>
        /// <param name="localName">The local name of the element.</param>
        /// <param name="ns">The namespace of the element.</param>
        /// <param name="textConstruct">The <see cref="AtomTextConstruct"/> value to be used as element content.</param>
        internal void WriteTextConstruct(string prefix, string localName, string ns, AtomTextConstruct textConstruct)
        {
            Debug.Assert(prefix != null, "prefix != null");
            Debug.Assert(!string.IsNullOrEmpty(localName), "!string.IsNullOrEmpty(localName)");
            Debug.Assert(!string.IsNullOrEmpty(ns), "!string.IsNullOrEmpty(ns)");

            this.XmlWriter.WriteStartElement(prefix, localName, ns);

            if (textConstruct != null)
            {
                AtomTextConstructKind textKind = textConstruct.Kind;

                this.XmlWriter.WriteAttributeString(AtomConstants.AtomTypeAttributeName, AtomValueUtils.ToString(textConstruct.Kind));

                string textValue = textConstruct.Text;
                if (textValue == null)
                {
                    textValue = String.Empty;
                }

                if (textKind == AtomTextConstructKind.Xhtml)
                {
                    ODataAtomWriterUtils.WriteRaw(this.XmlWriter, textValue);
                }
                else
                {
                    ODataAtomWriterUtils.WriteString(this.XmlWriter, textValue);
                }
            }

            this.XmlWriter.WriteEndElement();
        }
        internal void WriteServiceDocumentMetadata(ODataServiceDocument serviceDocument)
        {
            Debug.Assert(serviceDocument != null, "serviceDocument != null");

            AtomWorkspaceMetadata metadata = serviceDocument.GetAnnotation <AtomWorkspaceMetadata>();
            AtomTextConstruct     title    = null;

            if (metadata != null)
            {
                title = metadata.Title;
            }

            if (title == null)
            {
                title = new AtomTextConstruct {
                    Text = AtomConstants.AtomWorkspaceDefaultTitle
                };
            }

            if (this.UseServerFormatBehavior && title.Kind == AtomTextConstructKind.Text)
            {
                // For WCF DS server we must not write the type attribute, just a simple <atom:title>Default<atom:title>
                this.WriteElementWithTextContent(
                    AtomConstants.NonEmptyAtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace,
                    title.Text);
            }
            else
            {
                // <atom:title>title</atom:title>
                this.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title);
            }
        }
        internal void WriteServiceDocumentMetadata(ODataServiceDocument serviceDocument)
        {
            Debug.Assert(serviceDocument != null, "serviceDocument != null");

            AtomWorkspaceMetadata metadata = serviceDocument.GetAnnotation<AtomWorkspaceMetadata>();
            AtomTextConstruct title = null;
            if (metadata != null)
            {
                title = metadata.Title;
            }

            if (title == null)
            {
                title = new AtomTextConstruct { Text = AtomConstants.AtomWorkspaceDefaultTitle };
            }

            if (this.UseServerFormatBehavior && title.Kind == AtomTextConstructKind.Text)
            {
                // For WCF DS server we must not write the type attribute, just a simple <atom:title>Default<atom:title>
                this.WriteElementWithTextContent(
                    AtomConstants.NonEmptyAtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace,
                    title.Text);
            }
            else
            {
                // <atom:title>title</atom:title>
                this.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title);
            }
        }
        /// <summary>
        /// Writes an Xml element with the specified primitive value as content.
        /// </summary>
        /// <param name="prefix">The prefix for the element's namespace.</param>
        /// <param name="localName">The local name of the element.</param>
        /// <param name="ns">The namespace of the element.</param>
        /// <param name="textConstruct">The <see cref="AtomTextConstruct"/> value to be used as element content.</param>
        internal void WriteTextConstruct(string prefix, string localName, string ns, AtomTextConstruct textConstruct)
        {
            Debug.Assert(prefix != null, "prefix != null");
            Debug.Assert(!string.IsNullOrEmpty(localName), "!string.IsNullOrEmpty(localName)");
            Debug.Assert(!string.IsNullOrEmpty(ns), "!string.IsNullOrEmpty(ns)");

            this.XmlWriter.WriteStartElement(prefix, localName, ns);

            if (textConstruct != null)
            {
                AtomTextConstructKind textKind = textConstruct.Kind;

                this.XmlWriter.WriteAttributeString(AtomConstants.AtomTypeAttributeName, AtomValueUtils.ToString(textConstruct.Kind));

                string textValue = textConstruct.Text;
                if (textValue == null)
                {
                    textValue = String.Empty;
                }

                if (textKind == AtomTextConstructKind.Xhtml)
                {
                    ODataAtomWriterUtils.WriteRaw(this.XmlWriter, textValue);
                }
                else
                {
                    ODataAtomWriterUtils.WriteString(this.XmlWriter, textValue);
                }
            }

            this.XmlWriter.WriteEndElement();
        }
        public void PropertyGettersAndSettersTest()
        {
            AtomTextConstruct title = new AtomTextConstruct();

            AtomWorkspaceMetadata workspace = new AtomWorkspaceMetadata()
            {
                Title = title,
            };

            this.Assert.AreSame(title, workspace.Title, "Expected reference equal values for property 'Title'.");
        }
        public void PropertyGettersAndSettersTest()
        {
            AtomTextConstruct title = new AtomTextConstruct();
            string accept = "mime/type";
            AtomCategoriesMetadata categories = new AtomCategoriesMetadata();

            AtomResourceCollectionMetadata resourceCollection = new AtomResourceCollectionMetadata()
            {
                Title = title,
                Accept = accept,
                Categories = categories
            };

            this.Assert.AreSame(title, resourceCollection.Title, "Expected reference equal values for property 'Title'.");
            this.Assert.AreSame(accept, resourceCollection.Accept, "Expected reference equal values for property 'Accept'.");
            this.Assert.AreSame(categories, resourceCollection.Categories, "Expected reference equal values for property 'Categories'.");
        }
        /// <summary>
        /// Reads an atom:title element and adds the new information to <paramref name="odataServiceDocumentElement"/> and (if ATOM metadata reading is on) <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The collection metadata object to augment, or null if metadata reading is not on.</param>
        /// <param name="odataServiceDocumentElement">The non-null service document element info object being populated.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the title element.
        /// Post-Condition: Any                 - The next node after the title element.
        /// </remarks>
        internal void ReadTitleElementInCollection(AtomResourceCollectionMetadata collectionMetadata, ODataServiceDocumentElement odataServiceDocumentElement)
        {
            Debug.Assert(!this.ReadAtomMetadata || collectionMetadata != null, "collectionMetadata parameter should be non-null when ATOM metadata reading is enabled.");
            Debug.Assert(odataServiceDocumentElement != null, "collectionInfo != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomTitleElementName, "Expected element named 'title'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Element 'title' should be in the atom namespace.");

            AtomTextConstruct titleTextConstruct = this.ReadTitleElement();

            if (odataServiceDocumentElement.Name == null)
            {
                odataServiceDocumentElement.Name = titleTextConstruct.Text;
            }

            if (this.ReadAtomMetadata)
            {
                collectionMetadata.Title = titleTextConstruct;
            }
        }
        /// <summary>
        /// Read the ATOM text construct element.
        /// </summary>
        /// <returns>The element read represented as ATOM text construct.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - the element to read.
        /// Post-Condition: Any                 - the node after the element.
        /// </remarks>
        protected AtomTextConstruct ReadAtomTextConstruct()
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The element must be in ATOM namespace for this method to work.");
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomRightsElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomSummaryElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomSubtitleElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomTitleElementName,
                "Only atom:rights, atom:summary, atom:subtitle, and atom:title elements are supported by this method.");

            // TODO: Client ignores the type attribute and reads everything as plain text.
            AtomTextConstruct textConstruct = new AtomTextConstruct();

            string typeValue = null;
            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace) && string.CompareOrdinal(this.XmlReader.LocalName, AtomConstants.AtomTypeAttributeName) == 0)
                {
                    // type attribute
                    typeValue = this.XmlReader.Value;
                }
            }

            this.XmlReader.MoveToElement();
            if (typeValue == null)
            {
                textConstruct.Kind = AtomTextConstructKind.Text;
            }
            else
            {
                switch (typeValue)
                {
                    case AtomConstants.AtomTextConstructTextKind:
                        textConstruct.Kind = AtomTextConstructKind.Text;
                        break;
                    case AtomConstants.AtomTextConstructHtmlKind:
                        textConstruct.Kind = AtomTextConstructKind.Html;
                        break;
                    case AtomConstants.AtomTextConstructXHtmlKind:
                        textConstruct.Kind = AtomTextConstructKind.Xhtml;
                        break;
                    default:
                        throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidTextConstructKind(typeValue, this.XmlReader.LocalName));
                }
            }

            if (textConstruct.Kind == AtomTextConstructKind.Xhtml)
            {
                this.XmlReader.AssertNotBuffering();
                textConstruct.Text = this.XmlReader.ReadInnerXml();
            }
            else
            {
                textConstruct.Text = this.ReadElementStringValue();
            }

            return textConstruct;
        }
        /// <summary>
        /// Writes the ATOM metadata for a single entity set element.
        /// </summary>
        /// <param name="entitySetInfo">The entity set element to get the metadata for and write it.</param>
        internal void WriteEntitySetInfoMetadata(ODataEntitySetInfo entitySetInfo)
        {
            Debug.Assert(entitySetInfo != null, "collection != null");
            Debug.Assert(entitySetInfo.Url != null, "collection.Url should have been validated at this point");

            AtomResourceCollectionMetadata metadata = entitySetInfo.GetAnnotation <AtomResourceCollectionMetadata>();

            AtomTextConstruct title = null;

            if (metadata != null)
            {
                title = metadata.Title;
            }

            if (entitySetInfo.Name != null)
            {
                if (title == null)
                {
                    title = new AtomTextConstruct {
                        Text = entitySetInfo.Name
                    };
                }
                else if (string.CompareOrdinal(title.Text, entitySetInfo.Name) != 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataAtomServiceDocumentMetadataSerializer_ResourceCollectionNameAndTitleMismatch(entitySetInfo.Name, title.Text));
                }
            }

            // The ATOMPUB specification requires a title.
            // <atom:title>title</atom:title>
            // Note that this will write an empty atom:title element even if the title is null.
            if (this.UseServerFormatBehavior && title.Kind == AtomTextConstructKind.Text)
            {
                // For WCF DS server we must not write the type attribute, just a simple <atom:title>title<atom:title>
                this.WriteElementWithTextContent(
                    AtomConstants.NonEmptyAtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace,
                    title.Text);
            }
            else
            {
                this.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title);
            }

            if (metadata != null)
            {
                string accept = metadata.Accept;
                if (accept != null)
                {
                    // <app:accept>accept</app:accept>
                    this.WriteElementWithTextContent(
                        string.Empty,
                        AtomConstants.AtomPublishingAcceptElementName,
                        AtomConstants.AtomPublishingNamespace,
                        accept);
                }

                AtomCategoriesMetadata categories = metadata.Categories;
                if (categories != null)
                {
                    // <app:categories>
                    this.XmlWriter.WriteStartElement(string.Empty, AtomConstants.AtomPublishingCategoriesElementName, AtomConstants.AtomPublishingNamespace);

                    Uri    href       = categories.Href;
                    bool?  fixedValue = categories.Fixed;
                    string scheme     = categories.Scheme;
                    IEnumerable <AtomCategoryMetadata> categoriesCollection = categories.Categories;
                    if (href != null)
                    {
                        // Out of line categories document
                        if (fixedValue.HasValue || scheme != null ||
                            (categoriesCollection != null && categoriesCollection.Any()))
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues);
                        }

                        this.XmlWriter.WriteAttributeString(AtomConstants.AtomHRefAttributeName, this.UriToUrlAttributeValue(href));
                    }
                    else
                    {
                        // Inline categories document

                        // fixed='yes|no'
                        if (fixedValue.HasValue)
                        {
                            this.XmlWriter.WriteAttributeString(
                                AtomConstants.AtomPublishingFixedAttributeName,
                                fixedValue.Value ? AtomConstants.AtomPublishingFixedYesValue : AtomConstants.AtomPublishingFixedNoValue);
                        }

                        // scheme='scheme'
                        if (scheme != null)
                        {
                            this.XmlWriter.WriteAttributeString(AtomConstants.AtomCategorySchemeAttributeName, scheme);
                        }

                        if (categoriesCollection != null)
                        {
                            foreach (AtomCategoryMetadata category in categoriesCollection)
                            {
                                // <atom:category/>
                                this.WriteCategory(AtomConstants.NonEmptyAtomNamespacePrefix, category.Term, category.Scheme, category.Label);
                            }
                        }
                    }

                    // </app:categories>
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
        private IEnumerable<PayloadWriterTestDescriptor<AtomTextConstruct>> CreateTextConstructTestDescriptors()
        {
            AtomTextConstruct testTitle = new AtomTextConstruct { Text = null };
            AtomTextConstruct testTitle2 = string.Empty;
            AtomTextConstruct testTitle3 = "Test title";
            AtomTextConstruct testTitle4 = new AtomTextConstruct { Kind = AtomTextConstructKind.Text, Text = "Test title" };
            AtomTextConstruct testTitle5 = new AtomTextConstruct { Kind = AtomTextConstructKind.Html, Text = "Test title" };
            // NOTE: according to the spec '<' and '>' in Html payloads have to be escaped so the below would not be valid;
            //       we are not as strict so this works (and the string gets escaped)
            AtomTextConstruct testTitle6 = new AtomTextConstruct { Kind = AtomTextConstructKind.Html, Text = "<h2>Some headline</h2>" };
            AtomTextConstruct testTitle7 = new AtomTextConstruct { Kind = AtomTextConstructKind.Html, Text = "&lt;h2>Some headline&lt;/h2>" };
            // NOTE: according to the spec the text has to start with an <xhtml:div> element and thus this is not a valid
            //       XHtml payload; we are not as strict so this works fine.
            AtomTextConstruct testTitle8 = new AtomTextConstruct { Kind = AtomTextConstructKind.Xhtml, Text = "Test title" };
            AtomTextConstruct testTitle9 = new AtomTextConstruct { Kind = AtomTextConstructKind.Xhtml, Text = @"<div xmlns=""http://www.w3.org/1999/xhtml""><h2>Some headline</h2></div>" };

            var testCases = new[] {
                new { // null text
                    TextConstruct = testTitle,
                    Xml = @"<TextConstructElement type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @"""></TextConstructElement>",
                },
                new { // empty text
                    TextConstruct = testTitle2,
                    Xml = @"<TextConstructElement type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @"""></TextConstructElement>",
                },
                new { // non-empty text (kind = text plain, implicit)
                    TextConstruct = testTitle3,
                    Xml = @"<TextConstructElement type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle3.Text + @"</TextConstructElement>",
                },
                new { // non-empty text (kind = text plain, explicit)
                    TextConstruct = testTitle4,
                    Xml = @"<TextConstructElement type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle4.Text + @"</TextConstructElement>",
                },
                new { // non-empty text (kind = Html)
                    TextConstruct = testTitle5,
                    Xml = @"<TextConstructElement type=""html"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle5.Text + @"</TextConstructElement>",
                },
                new { // unescaped Html text (kind = Html)
                    TextConstruct = testTitle6,
                    Xml = @"<TextConstructElement type=""html"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">&lt;h2&gt;Some headline&lt;/h2&gt;</TextConstructElement>",
                },
                new { // escaped Html text (kind = Html)
                    TextConstruct = testTitle7,
                    Xml = @"<TextConstructElement type=""html"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">&amp;lt;h2&gt;Some headline&amp;lt;/h2&gt;</TextConstructElement>",
                },
                new { // plain text (kind = XHtml)
                    TextConstruct = testTitle8,
                    Xml = @"<TextConstructElement type=""xhtml"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle8.Text + @"</TextConstructElement>",
                },
                new { // xhtml non-escaped text (kind = XHtml)
                    TextConstruct = testTitle9,
                    Xml = @"<TextConstructElement type=""xhtml"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle9.Text + @"</TextConstructElement>",
                },
            };

            // Convert test cases to test descriptions
            return testCases.Select(testCase =>
            {
                return new PayloadWriterTestDescriptor<AtomTextConstruct>(this.Settings, testCase.TextConstruct, testConfiguration =>
                    new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.Xml, FragmentExtractor = (e) => e });
            });
        }
예제 #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();
                }
            }
        }
예제 #12
0
        /// <summary>
        /// Read the ATOM text construct element.
        /// </summary>
        /// <returns>The element read represented as ATOM text construct.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - the element to read.
        /// Post-Condition: Any                 - the node after the element.
        /// </remarks>
        protected AtomTextConstruct ReadAtomTextConstruct()
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The element must be in ATOM namespace for this method to work.");
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomRightsElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomSummaryElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomSubtitleElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomTitleElementName,
                "Only atom:rights, atom:summary, atom:subtitle, and atom:title elements are supported by this method.");

            // TODO: Client ignores the type attribute and reads everything as plain text.
            AtomTextConstruct textConstruct = new AtomTextConstruct();

            string typeValue = null;

            while (this.XmlReader.MoveToNextAttribute())
            {
                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace) && string.CompareOrdinal(this.XmlReader.LocalName, AtomConstants.AtomTypeAttributeName) == 0)
                {
                    // type attribute
                    typeValue = this.XmlReader.Value;
                }
            }

            this.XmlReader.MoveToElement();
            if (typeValue == null)
            {
                textConstruct.Kind = AtomTextConstructKind.Text;
            }
            else
            {
                switch (typeValue)
                {
                case AtomConstants.AtomTextConstructTextKind:
                    textConstruct.Kind = AtomTextConstructKind.Text;
                    break;

                case AtomConstants.AtomTextConstructHtmlKind:
                    textConstruct.Kind = AtomTextConstructKind.Html;
                    break;

                case AtomConstants.AtomTextConstructXHtmlKind:
                    textConstruct.Kind = AtomTextConstructKind.Xhtml;
                    break;

                default:
                    throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidTextConstructKind(typeValue, this.XmlReader.LocalName));
                }
            }

            if (textConstruct.Kind == AtomTextConstructKind.Xhtml)
            {
                this.XmlReader.AssertNotBuffering();
                textConstruct.Text = this.XmlReader.ReadInnerXml();
            }
            else
            {
                textConstruct.Text = this.ReadElementStringValue();
            }

            return(textConstruct);
        }
        /// <summary>
        /// Writes the ATOM metadata for a single entity set element.
        /// </summary>
        /// <param name="entitySetInfo">The entity set element to get the metadata for and write it.</param>
        internal void WriteEntitySetInfoMetadata(ODataEntitySetInfo entitySetInfo)
        {
            Debug.Assert(entitySetInfo != null, "collection != null");
            Debug.Assert(entitySetInfo.Url != null, "collection.Url should have been validated at this point");

            AtomResourceCollectionMetadata metadata = entitySetInfo.GetAnnotation<AtomResourceCollectionMetadata>();

            AtomTextConstruct title = null;
            if (metadata != null)
            {
                title = metadata.Title;
            }

            if (entitySetInfo.Name != null)
            {
                if (title == null)
                {
                    title = new AtomTextConstruct { Text = entitySetInfo.Name };
                }
                else if (string.CompareOrdinal(title.Text, entitySetInfo.Name) != 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataAtomServiceDocumentMetadataSerializer_ResourceCollectionNameAndTitleMismatch(entitySetInfo.Name, title.Text));
                }
            }

            // The ATOMPUB specification requires a title.
            // <atom:title>title</atom:title>
            // Note that this will write an empty atom:title element even if the title is null.
            if (this.UseServerFormatBehavior && title.Kind == AtomTextConstructKind.Text)
            {
                // For WCF DS server we must not write the type attribute, just a simple <atom:title>title<atom:title>
                this.WriteElementWithTextContent(
                    AtomConstants.NonEmptyAtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace,
                    title.Text);
            }
            else
            {
                this.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title);
            }

            if (metadata != null)
            {
                string accept = metadata.Accept;
                if (accept != null)
                {
                    // <app:accept>accept</app:accept>
                    this.WriteElementWithTextContent(
                        string.Empty,
                        AtomConstants.AtomPublishingAcceptElementName,
                        AtomConstants.AtomPublishingNamespace,
                        accept);
                }

                AtomCategoriesMetadata categories = metadata.Categories;
                if (categories != null)
                {
                    // <app:categories>
                    this.XmlWriter.WriteStartElement(string.Empty, AtomConstants.AtomPublishingCategoriesElementName, AtomConstants.AtomPublishingNamespace);

                    Uri href = categories.Href;
                    bool? fixedValue = categories.Fixed;
                    string scheme = categories.Scheme;
                    IEnumerable<AtomCategoryMetadata> categoriesCollection = categories.Categories;
                    if (href != null)
                    {
                        // Out of line categories document
                        if (fixedValue.HasValue || scheme != null ||
                            (categoriesCollection != null && categoriesCollection.Any()))
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues);
                        }

                        this.XmlWriter.WriteAttributeString(AtomConstants.AtomHRefAttributeName, this.UriToUrlAttributeValue(href));
                    }
                    else
                    {
                        // Inline categories document

                        // fixed='yes|no'
                        if (fixedValue.HasValue)
                        {
                            this.XmlWriter.WriteAttributeString(
                                AtomConstants.AtomPublishingFixedAttributeName,
                                fixedValue.Value ? AtomConstants.AtomPublishingFixedYesValue : AtomConstants.AtomPublishingFixedNoValue);
                        }

                        // scheme='scheme'
                        if (scheme != null)
                        {
                            this.XmlWriter.WriteAttributeString(AtomConstants.AtomCategorySchemeAttributeName, scheme);
                        }

                        if (categoriesCollection != null)
                        {
                            foreach (AtomCategoryMetadata category in categoriesCollection)
                            {
                                // <atom:category/>
                                this.WriteCategory(AtomConstants.NonEmptyAtomNamespacePrefix, category.Term, category.Scheme, category.Label);
                            }
                        }
                    }

                    // </app:categories>
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
예제 #14
0
        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>
 /// Visits an ATOM text construct.
 /// </summary>
 /// <param name="atomTextConstruct">The text construct to visit.</param>
 protected virtual void VisitAtomTextConstruct(AtomTextConstruct atomTextConstruct)
 {
 }