/// <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); } }
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 = "<h2>Some headline</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 + @"""><h2>Some headline</h2></TextConstructElement>", }, new { // escaped Html text (kind = Html) TextConstruct = testTitle7, Xml = @"<TextConstructElement type=""html"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">&lt;h2>Some headline&lt;/h2></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 }); }); }
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(); } } }
/// <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(); } } }
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) { }