/// <summary> /// Adds a new link to entry metadata. /// </summary> /// <param name="entryMetadata">The entry metadata to add the link to.</param> /// <param name="linkMetadata">The link metadata to add.</param> internal static void AddLink(this AtomEntryMetadata entryMetadata, AtomLinkMetadata linkMetadata) { Debug.Assert(entryMetadata != null, "entryMetadata != null"); Debug.Assert(linkMetadata != null, "linkMetadata != null"); entryMetadata.Links = entryMetadata.Links.ConcatToReadOnlyEnumerable("Ref", linkMetadata); }
/// <summary> /// Writes the navigation link's start element and atom metadata. /// </summary> /// <param name="navigationLink">The navigation link to write.</param> /// <param name="navigationLinkUrlOverride">Url to use for the navigation link. If this is specified the Url property on the <paramref name="navigationLink"/> /// will be ignored. If this parameter is null, the Url from the navigation link is used.</param> internal void WriteNavigationLinkStart(ODataNavigationLink navigationLink, Uri navigationLinkUrlOverride) { Debug.Assert(navigationLink != null, "navigationLink != null"); Debug.Assert(!string.IsNullOrEmpty(navigationLink.Name), "The navigation link name was not verified yet."); Debug.Assert(navigationLink.Url != null, "The navigation link Url was not verified yet."); Debug.Assert(navigationLink.IsCollection.HasValue, "navigationLink.IsCollection.HasValue"); if (navigationLink.AssociationLinkUrl != null) { // TODO:Association Link - Add back support for customizing association link element in Atom this.WriteAssociationLink(navigationLink.Name, navigationLink.AssociationLinkUrl, null); } // <atom:link> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); string linkRelation = AtomUtils.ComputeODataNavigationLinkRelation(navigationLink); string linkType = AtomUtils.ComputeODataNavigationLinkType(navigationLink); string linkTitle = navigationLink.Name; Uri navigationLinkUrl = navigationLinkUrlOverride ?? navigationLink.Url; AtomLinkMetadata linkMetadata = navigationLink.GetAnnotation <AtomLinkMetadata>(); AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(linkMetadata, linkRelation, navigationLinkUrl, linkTitle, linkType); this.atomEntryMetadataSerializer.WriteAtomLinkAttributes(mergedMetadata, null /* etag */); }
/// <summary> /// Write the metadata for an OData association link; makes sure any duplicate of the link's values duplicated in metadata are equal. /// </summary> /// <param name="navigationPropertyName">The name of the navigation property whose association link is being written.</param> /// <param name="associationLinkUrl">The association link url to write.</param> /// <param name="associationLinkMetadata">Atom metadata about this link element. This can be used to customized the link element with additional XML attributes.</param> internal void WriteAssociationLink(string navigationPropertyName, Uri associationLinkUrl, AtomLinkMetadata associationLinkMetadata) { AtomLinkMetadata linkMetadata = associationLinkMetadata; string linkRelation = AtomUtils.ComputeODataAssociationLinkRelation(navigationPropertyName); AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(linkMetadata, linkRelation, associationLinkUrl, navigationPropertyName, MimeConstants.MimeApplicationXml); this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, null /* etag*/); }
/// <summary> /// Writes a stream property to the ATOM payload /// </summary> /// <param name="streamProperty">The stream property to create the payload for.</param> /// <param name="owningType">The <see cref="IEdmEntityType"/> instance for which the stream property defined on.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> internal void WriteStreamProperty( ODataProperty streamProperty, IEdmEntityType owningType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { Debug.Assert(streamProperty != null, "Stream property must not be null."); Debug.Assert(streamProperty.Value != null, "The media resource of the stream property must not be null."); WriterValidationUtils.ValidatePropertyNotNull(streamProperty); string propertyName = streamProperty.Name; if (projectedProperties.ShouldSkipProperty(propertyName)) { return; } WriterValidationUtils.ValidatePropertyName(propertyName); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(streamProperty); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(streamProperty.Name, owningType); WriterValidationUtils.ValidateStreamReferenceProperty(streamProperty, edmProperty, this.Version, this.WritingResponse); ODataStreamReferenceValue streamReferenceValue = (ODataStreamReferenceValue)streamProperty.Value; WriterValidationUtils.ValidateStreamReferenceValue(streamReferenceValue, false /*isDefaultStream*/); if (owningType != null && owningType.IsOpen && edmProperty == null) { ValidationUtils.ValidateOpenPropertyValue(streamProperty.Name, streamReferenceValue); } AtomStreamReferenceMetadata streamReferenceMetadata = streamReferenceValue.GetAnnotation <AtomStreamReferenceMetadata>(); string contentType = streamReferenceValue.ContentType; string linkTitle = streamProperty.Name; Uri readLink = streamReferenceValue.ReadLink; if (readLink != null) { string readLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, false); AtomLinkMetadata readLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.SelfLink; AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(readLinkMetadata, readLinkRelation, readLink, linkTitle, contentType); this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, null /* etag */); } Uri editLink = streamReferenceValue.EditLink; if (editLink != null) { string editLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, true); AtomLinkMetadata editLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink; AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(editLinkMetadata, editLinkRelation, editLink, linkTitle, contentType); this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, streamReferenceValue.ETag); } }
/// <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> /// Reads an atom:link element into the Links collection of feed metadata (i.e., links that are not special to the OData protocol). /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: Any - the node after the atom:link element which was read. /// </remarks> private void ReadLinkElementIntoLinksCollection(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomLinkElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:link elements can be read by this method."); // By sending nulls to ReadAtomLinkElementInFeed(), the method will read and store values for href and rel from the wire (inside of using parameter overrides). AtomLinkMetadata linkMetadata = this.ReadAtomLinkElementInFeed(null, null); atomFeedMetadata.AddLink(linkMetadata); }
/// <summary> /// Writes a feed link. /// </summary> /// <param name="feed">The feed that contains the link.</param> /// <param name="relation">Relation attribute of the link.</param> /// <param name="href">href attribute of the link.</param> /// <param name="getLinkMetadata">Function to get the AtomLinkMetadata for the feed link.</param> internal void WriteFeedLink(ODataFeed feed, string relation, Uri href, Func <AtomFeedMetadata, AtomLinkMetadata> getLinkMetadata) { AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>(); AtomLinkMetadata mergedLink = ODataAtomWriterMetadataUtils.MergeLinkMetadata( getLinkMetadata(feedMetadata), relation, href, null, /*title*/ null /*mediaType*/); this.atomFeedMetadataSerializer.WriteAtomLink(mergedLink, null); }
public static AtomLinkMetadata Atom(this ODataNavigationLink navigationLink) { ExceptionUtils.CheckArgumentNotNull(navigationLink, "navigationLink"); AtomLinkMetadata linkMetadata = navigationLink.GetAnnotation<AtomLinkMetadata>(); if (linkMetadata == null) { linkMetadata = new AtomLinkMetadata(); navigationLink.SetAnnotation(linkMetadata); } return linkMetadata; }
/// <summary> /// Write the metadata of a link in ATOM format /// </summary> /// <param name="linkMetadata">The link metadata to write.</param> /// <param name="etag">The (optional) ETag for a link.</param> internal void WriteAtomLinkAttributes(AtomLinkMetadata linkMetadata, string etag) { Debug.Assert(linkMetadata != null, "Link metadata must not be null."); string linkHref = linkMetadata.Href == null ? null : this.UriToUrlAttributeValue(linkMetadata.Href); this.WriteAtomLinkMetadataAttributes(linkMetadata.Relation, linkHref, linkMetadata.HrefLang, linkMetadata.Title, linkMetadata.MediaType, linkMetadata.Length); if (etag != null) { ODataAtomWriterUtils.WriteETag(this.XmlWriter, etag); } }
/// <summary> /// Copy constructor. /// </summary> /// <param name="other">The <see cref="AtomLinkMetadata"/> instance to copy the values from; can be null.</param> internal AtomLinkMetadata(AtomLinkMetadata other) { if (other == null) { return; } this.Relation = other.Relation; this.Href = other.Href; this.HrefLang = other.HrefLang; this.Title = other.Title; this.MediaType = other.MediaType; this.Length = other.Length; }
public static AtomLinkMetadata Atom(this ODataNavigationLink navigationLink) { ExceptionUtils.CheckArgumentNotNull(navigationLink, "navigationLink"); AtomLinkMetadata linkMetadata = navigationLink.GetAnnotation <AtomLinkMetadata>(); if (linkMetadata == null) { linkMetadata = new AtomLinkMetadata(); navigationLink.SetAnnotation(linkMetadata); } return(linkMetadata); }
/// <summary> /// Write the metadata of a link in ATOM format /// </summary> /// <param name="linkMetadata">The link metadata to write.</param> /// <param name="etag">The (optional) ETag for a link.</param> internal void WriteAtomLink(AtomLinkMetadata linkMetadata, string etag) { Debug.Assert(linkMetadata != null, "Link metadata must not be null."); // <atom:link ... this.XmlWriter.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); // write the attributes of the link this.WriteAtomLinkAttributes(linkMetadata, etag); // </atom:link> this.XmlWriter.WriteEndElement(); }
/// <summary> /// Writes the self or edit link. /// </summary> /// <param name="link">Uri object for the link.</param> /// <param name="linkMetadata">The atom link metadata for the link to specify title, type, hreflang and length of the link.</param> /// <param name="linkRelation">Relationship value. Either "edit" or "self".</param> private void WriteReadOrEditLink( Uri link, AtomLinkMetadata linkMetadata, string linkRelation) { if (link != null) { AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( linkMetadata, linkRelation, link, null /* title */, null /* media type */); this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, null /* etag */); } }
/// <summary> /// Writes the edit-media link for an entry. /// </summary> /// <param name="mediaResource">The media resource representing the MR of the entry to write.</param> internal void WriteEntryMediaEditLink(ODataStreamReferenceValue mediaResource) { Debug.Assert(mediaResource != null, "mediaResource != null"); Uri mediaEditLink = mediaResource.EditLink; Debug.Assert(mediaEditLink != null || mediaResource.ETag == null, "The default stream edit link and etag should have been validated by now."); if (mediaEditLink != null) { AtomStreamReferenceMetadata streamReferenceMetadata = mediaResource.GetAnnotation <AtomStreamReferenceMetadata>(); AtomLinkMetadata mediaEditMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink; AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( mediaEditMetadata, AtomConstants.AtomEditMediaRelationAttributeValue, mediaEditLink, null /* title */, null /* mediaType */); this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, mediaResource.ETag); } }
/// <summary> /// Reads the atom:link element and returns a new ATOM link metadata object. /// </summary> /// <param name="relation">The value of the rel attribute for the link element.</param> /// <param name="hrefStringValue">The value of the href attribute for the link element.</param> /// <returns>An <see cref="AtomLinkMetadata"/> instance storing the information about this link.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: Any - the node after the ATOM element which was read. /// </remarks> internal AtomLinkMetadata ReadAtomLinkElementInFeed(string relation, string hrefStringValue) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomLinkElementName, "Only atom:link element can be read by this method."); AtomLinkMetadata linkMetadata = new AtomLinkMetadata { Relation = relation, Href = hrefStringValue == null ? null : this.ProcessUriFromPayload(hrefStringValue, this.XmlReader.XmlBaseUri) }; // Read the attributes while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { switch (this.XmlReader.LocalName) { case AtomConstants.AtomLinkTypeAttributeName: linkMetadata.MediaType = this.XmlReader.Value; break; case AtomConstants.AtomLinkHrefLangAttributeName: linkMetadata.HrefLang = this.XmlReader.Value; break; case AtomConstants.AtomLinkTitleAttributeName: linkMetadata.Title = this.XmlReader.Value; break; case AtomConstants.AtomLinkLengthAttributeName: string lengthStringValue = this.XmlReader.Value; int length; if (int.TryParse(lengthStringValue, NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out length)) { linkMetadata.Length = length; } else { throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidLinkLengthValue(lengthStringValue)); } break; case AtomConstants.AtomLinkRelationAttributeName: // Only store this rel value if linkMetadata.Relation was not set yet. // Note: The value supplied via the parameter "relation" takes priority // over what we read in the XML at this point. This is because // of situations such as having an IANA namespace prefix on the rel // value (in which case we don't store the literal rel value as it // appears in the xml, but just the part after the IANA prefix). if (linkMetadata.Relation == null) { linkMetadata.Relation = this.XmlReader.Value; } break; case AtomConstants.AtomLinkHrefAttributeName: // Only store the href value if linkMetadata.Href was not set yet. if (linkMetadata.Href == null) { linkMetadata.Href = this.ProcessUriFromPayload(this.XmlReader.Value, this.XmlReader.XmlBaseUri); } break; default: // Ignore all other attributes. break; } } } this.XmlReader.Skip(); return(linkMetadata); }
/// <summary> /// Converts the Object Model representation of Atom link metadata into appropriate annotations for a payload element representing a link. /// </summary> /// <param name="linkMetadata">The Atom link metadata, in Object Model representation, to convert.</param> /// <param name="linkPayloadElement">The link payload element to annotate.</param> /// <remarks>This method is only for use with payload elements that represent links, as it will skip over the root link annotation.</remarks> private static void ConvertAtomLinkChildrenMetadata(AtomLinkMetadata linkMetadata, ODataPayloadElement linkPayloadElement) { ExceptionUtilities.CheckArgumentNotNull(linkMetadata, "linkMetadata"); ExceptionUtilities.CheckArgumentNotNull(linkPayloadElement, "linkPayloadElement"); // Since the payload element already represents a link, we annotate a temporary element // and copy the "children" annotations onto the actual payload element. var tempPayloadElement = new EntityInstance(); ExceptionUtilities.Assert(!tempPayloadElement.Annotations.OfType<XmlTreeAnnotation>().Any(), "Payload element should not have XmlTreeAnnotations after construction"); ConvertAtomLinkMetadata(linkMetadata, tempPayloadElement); XmlTreeAnnotation linkAnnotation = tempPayloadElement.Annotations.OfType<XmlTreeAnnotation>().Single(); foreach (XmlTreeAnnotation childAnnotation in linkAnnotation.Children) { linkPayloadElement.AddAnnotation(childAnnotation); } }
/// <summary> /// Converts the Object Model representation of Atom link metadata into appropriate annotations for a link ODataPayloadElement. /// </summary> /// <param name="linkMetadata">The Atom link metadata, in Object Model representation, to convert.</param> /// <param name="payloadElement">The payload element to annotate.</param> private static void ConvertAtomLinkMetadata(AtomLinkMetadata linkMetadata, ODataPayloadElement payloadElement) { ExceptionUtilities.CheckArgumentNotNull(linkMetadata, "linkMetadata"); ExceptionUtilities.CheckArgumentNotNull(payloadElement, "link"); string lengthValue = linkMetadata.Length.HasValue ? linkMetadata.Length.Value.ToString() : null; payloadElement.AtomLink(linkMetadata.Href == null ? null : linkMetadata.Href.OriginalString, linkMetadata.Relation, linkMetadata.MediaType, linkMetadata.HrefLang, linkMetadata.Title, lengthValue); }
/// <summary> /// Creates a new <see cref="AtomLinkMetadata"/> instance by merging the given /// <paramref name="metadata"/> (if any) with the specified <paramref name="href"/>, /// <paramref name="relation"/> and (optional) <paramref name="title"/>. /// </summary> /// <param name="metadata">The metadata to merge with the <paramref name="href"/>, <paramref name="relation"/> and (optional) <paramref name="title"/>.</param> /// <param name="relation">The relation to use in the merged metadata.</param> /// <param name="href">The href to use in the merged metadata.</param> /// <param name="title">The (optional) title to use in the merged metadata.</param> /// <param name="mediaType">The (optional) media type to use in the merged metadata.</param> /// <returns>A new <see cref="AtomLinkMetadata"/> instance created by merging all the arguments.</returns> /// <remarks> /// If the <paramref name="metadata"/> already holds values for <paramref name="href"/>, /// <paramref name="relation"/>, <paramref name="title"/>, or <paramref name="mediaType"/> this method validates that they /// are the same as the ones specified in the method arguments. /// </remarks> internal static AtomLinkMetadata MergeLinkMetadata( AtomLinkMetadata metadata, string relation, Uri href, string title, string mediaType) { Debug.Assert(relation != null, "relation != null"); AtomLinkMetadata mergedMetadata = new AtomLinkMetadata(metadata); // set the relation string metadataRelation = mergedMetadata.Relation; if (metadataRelation != null) { // validate that the relations are the same if (string.CompareOrdinal(relation, metadataRelation) != 0) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkRelationsMustMatch(relation, metadataRelation)); } } else { mergedMetadata.Relation = relation; } // set the href if it was specified if (href != null) { Uri metadataHref = mergedMetadata.Href; if (metadataHref != null) { // validate that the hrefs are the same if (!href.Equals(metadataHref)) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkHrefsMustMatch(href, metadataHref)); } } else { mergedMetadata.Href = href; } } // set the title if it was specified if (title != null) { string metadataTitle = mergedMetadata.Title; if (metadataTitle != null) { // validate that the relations are the same if (string.CompareOrdinal(title, metadataTitle) != 0) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkTitlesMustMatch(title, metadataTitle)); } } else { mergedMetadata.Title = title; } } // set the content type if it was specified if (mediaType != null) { string metadataMediaType = mergedMetadata.MediaType; if (metadataMediaType != null) { // validate that the relations are the same if (!HttpUtils.CompareMediaTypeNames(mediaType, metadataMediaType)) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkMediaTypesMustMatch(mediaType, metadataMediaType)); } } else { mergedMetadata.MediaType = mediaType; } } return mergedMetadata; }
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 link metadata. /// </summary> /// <param name="atomLinkMetadata">The link metadata to visit.</param> protected virtual void VisitAtomLinkMetadata(AtomLinkMetadata atomLinkMetadata) { }
/// <summary> /// Reads the atom:link element and returns a new ATOM link metadata object. /// </summary> /// <param name="relation">The value of the rel attribute for the link element.</param> /// <param name="hrefStringValue">The value of the href attribute for the link element.</param> /// <returns>An <see cref="AtomLinkMetadata"/> instance storing the information about this link.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: Any - the node after the ATOM element which was read. /// </remarks> internal AtomLinkMetadata ReadAtomLinkElementInFeed(string relation, string hrefStringValue) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomLinkElementName, "Only atom:link element can be read by this method."); AtomLinkMetadata linkMetadata = new AtomLinkMetadata { Relation = relation, Href = hrefStringValue == null ? null : this.ProcessUriFromPayload(hrefStringValue, this.XmlReader.XmlBaseUri) }; // Read the attributes while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { switch (this.XmlReader.LocalName) { case AtomConstants.AtomLinkTypeAttributeName: linkMetadata.MediaType = this.XmlReader.Value; break; case AtomConstants.AtomLinkHrefLangAttributeName: linkMetadata.HrefLang = this.XmlReader.Value; break; case AtomConstants.AtomLinkTitleAttributeName: linkMetadata.Title = this.XmlReader.Value; break; case AtomConstants.AtomLinkLengthAttributeName: string lengthStringValue = this.XmlReader.Value; int length; if (int.TryParse(lengthStringValue, NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out length)) { linkMetadata.Length = length; } else { throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidLinkLengthValue(lengthStringValue)); } break; case AtomConstants.AtomLinkRelationAttributeName: // Only store this rel value if linkMetadata.Relation was not set yet. // Note: The value supplied via the parameter "relation" takes priority // over what we read in the XML at this point. This is because // of situations such as having an IANA namespace prefix on the rel // value (in which case we don't store the literal rel value as it // appears in the xml, but just the part after the IANA prefix). if (linkMetadata.Relation == null) { linkMetadata.Relation = this.XmlReader.Value; } break; case AtomConstants.AtomLinkHrefAttributeName: // Only store the href value if linkMetadata.Href was not set yet. if (linkMetadata.Href == null) { linkMetadata.Href = this.ProcessUriFromPayload(this.XmlReader.Value, this.XmlReader.XmlBaseUri); } break; default: // Ignore all other attributes. break; } } } this.XmlReader.Skip(); return linkMetadata; }
internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten) { Debug.Assert(feedMetadata != null, "Feed metadata must not be null!"); // <atom:id>text</atom:id> // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself; // if used in the context of an <atom:source> element it is stored in metadata Uri id = feed == null ? feedMetadata.SourceId : feed.Id; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, id == null ? null : UriUtils.UriToString(id)); // <atom:title>text</atom:title> // NOTE: write an empty element if no title is specified since the element is required this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title); if (feedMetadata.Subtitle != null) { // <atom:subtitle>text</atom:subtitle> this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload. string updated = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updated); AtomLinkMetadata selfLinkMetadata = feedMetadata.SelfLink; if (selfLinkMetadata != null) { AtomLinkMetadata mergedSelfLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( selfLinkMetadata, AtomConstants.AtomSelfRelationAttributeValue, null /* href */, null /* title */, null /* media type */); this.WriteAtomLink(mergedSelfLinkMetadata, null /*etag*/); } IEnumerable <AtomLinkMetadata> links = feedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { // DeltaLink is written from ODataFeed, so it shouldn't be written again from AtomFeedMetadata. if (link.Relation != AtomConstants.AtomDeltaRelationAttributeValue) { // <atom:link>...</atom:link> this.WriteAtomLink(link, null /* etag */); } } } IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { // <atom:category term="..." scheme="..." label="..."></atom:category> this.WriteCategory(category); } } Uri logo = feedMetadata.Logo; if (logo != null) { // <atom:logo>Uri</atom:logo> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLogoElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(logo)); } if (feedMetadata.Rights != null) { // <atom:rights>rights</atom:rights> this.WriteTextConstruct( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, feedMetadata.Rights); } IEnumerable <AtomPersonMetadata> contributors = feedMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { // <atom:contributor>contributor data</atom:contributor> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(contributor); this.XmlWriter.WriteEndElement(); } } AtomGeneratorMetadata generator = feedMetadata.Generator; if (generator != null) { // <atom:generator uri="..." version="...">name</atom:generator> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace); if (generator.Uri != null) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, this.UriToUrlAttributeValue(generator.Uri)); } if (!string.IsNullOrEmpty(generator.Version)) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version); } ODataAtomWriterUtils.WriteString(this.XmlWriter, generator.Name); this.XmlWriter.WriteEndElement(); } Uri icon = feedMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(icon)); } IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors; authorWritten = false; if (authors != null) { foreach (AtomPersonMetadata author in authors) { // <atom:author>author data</atom:author> authorWritten = true; this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(author); this.XmlWriter.WriteEndElement(); } } }
/// <summary> /// Reads the atom:link element in the entry content. /// </summary> /// <param name="relation">The value of the rel attribute for the link element.</param> /// <param name="hrefStringValue">The value of the href attribute for the link element.</param> /// <returns>An <see cref="AtomLinkMetadata"/> instance storing the information about this link, or null if link info doesn't need to be stored.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: XmlNodeType.Element (atom:link) - the atom:link element which was read. /// </remarks> internal AtomLinkMetadata ReadAtomLinkElementInEntryContent(string relation, string hrefStringValue) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomLinkElementName, "Only atom:link element can be read by this method."); AtomLinkMetadata linkMetadata = null; if (this.ReadAtomMetadata) { linkMetadata = new AtomLinkMetadata(); linkMetadata.Relation = relation; if (this.ReadAtomMetadata) { linkMetadata.Href = hrefStringValue == null ? null : this.ProcessUriFromPayload(hrefStringValue, this.XmlReader.XmlBaseUri); } // Read the attributes while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { // Note that it's OK to store values which we don't validate in any way even if we might not need them. // The EPM reader will ignore them if they're not needed and the fact that we don't validate them means that there are no observable differences // if we store them. It keeps the code simpler (less ifs). switch (this.XmlReader.LocalName) { case AtomConstants.AtomLinkTypeAttributeName: linkMetadata.MediaType = this.XmlReader.Value; break; case AtomConstants.AtomLinkHrefLangAttributeName: linkMetadata.HrefLang = this.XmlReader.Value; break; case AtomConstants.AtomLinkTitleAttributeName: linkMetadata.Title = this.XmlReader.Value; break; case AtomConstants.AtomLinkLengthAttributeName: // We must NOT try to parse the value into a number if we don't need it for ATOM metadata. if (this.ReadAtomMetadata) { string lengthStringValue = this.XmlReader.Value; int length; if (int.TryParse(lengthStringValue, NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out length)) { linkMetadata.Length = length; } else { throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidLinkLengthValue(lengthStringValue)); } } break; default: // Ignore all other attributes. break; } } } } this.XmlReader.MoveToElement(); return linkMetadata; }
private static AtomLinkMetadata CreateLinkMetadata(IEnumerable<XmlTreeAnnotation> children) { AtomLinkMetadata metadata = new AtomLinkMetadata(); bool initialized = false; foreach (XmlTreeAnnotation epmTree in children.Where(child => child.IsAttribute)) { string localName = epmTree.LocalName; if (localName == TestAtomConstants.AtomLinkHrefAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.Href = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue); initialized = true; } else if (localName == TestAtomConstants.AtomLinkHrefLangAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.HrefLang = epmTree.PropertyValue; initialized = true; } else if (localName == TestAtomConstants.AtomLinkLengthAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.Length = string.IsNullOrEmpty(epmTree.PropertyValue) ? (int?)null : int.Parse(epmTree.PropertyValue); initialized = true; } else if (localName == TestAtomConstants.AtomLinkRelationAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.Relation = epmTree.PropertyValue; initialized = true; } else if (localName == TestAtomConstants.AtomLinkTitleAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.Title = epmTree.PropertyValue; initialized = true; } else if (localName == TestAtomConstants.AtomLinkTypeAttributeName) { Debug.Assert(epmTree.IsAttribute); metadata.MediaType = epmTree.PropertyValue; initialized = true; } else { throw new NotSupportedException("Unsupported metadata '" + localName + "' found for a link."); } } return initialized ? metadata : null; }
/// <summary> /// Creates a new <see cref="AtomLinkMetadata"/> instance by merging the given /// <paramref name="metadata"/> (if any) with the specified <paramref name="href"/>, /// <paramref name="relation"/> and (optional) <paramref name="title"/>. /// </summary> /// <param name="metadata">The metadata to merge with the <paramref name="href"/>, <paramref name="relation"/> and (optional) <paramref name="title"/>.</param> /// <param name="relation">The relation to use in the merged metadata.</param> /// <param name="href">The href to use in the merged metadata.</param> /// <param name="title">The (optional) title to use in the merged metadata.</param> /// <param name="mediaType">The (optional) media type to use in the merged metadata.</param> /// <returns>A new <see cref="AtomLinkMetadata"/> instance created by merging all the arguments.</returns> /// <remarks> /// If the <paramref name="metadata"/> already holds values for <paramref name="href"/>, /// <paramref name="relation"/>, <paramref name="title"/>, or <paramref name="mediaType"/> this method validates that they /// are the same as the ones specified in the method arguments. /// </remarks> internal static AtomLinkMetadata MergeLinkMetadata( AtomLinkMetadata metadata, string relation, Uri href, string title, string mediaType) { Debug.Assert(relation != null, "relation != null"); AtomLinkMetadata mergedMetadata = new AtomLinkMetadata(metadata); // set the relation string metadataRelation = mergedMetadata.Relation; if (metadataRelation != null) { // validate that the relations are the same if (string.CompareOrdinal(relation, metadataRelation) != 0) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkRelationsMustMatch(relation, metadataRelation)); } } else { mergedMetadata.Relation = relation; } // set the href if it was specified if (href != null) { Uri metadataHref = mergedMetadata.Href; if (metadataHref != null) { // validate that the hrefs are the same if (!href.Equals(metadataHref)) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkHrefsMustMatch(href, metadataHref)); } } else { mergedMetadata.Href = href; } } // set the title if it was specified if (title != null) { string metadataTitle = mergedMetadata.Title; if (metadataTitle != null) { // validate that the relations are the same if (string.CompareOrdinal(title, metadataTitle) != 0) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkTitlesMustMatch(title, metadataTitle)); } } else { mergedMetadata.Title = title; } } // set the content type if it was specified if (mediaType != null) { string metadataMediaType = mergedMetadata.MediaType; if (metadataMediaType != null) { // validate that the relations are the same if (!HttpUtils.CompareMediaTypeNames(mediaType, metadataMediaType)) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkMediaTypesMustMatch(mediaType, metadataMediaType)); } } else { mergedMetadata.MediaType = mediaType; } } return(mergedMetadata); }
/// <summary> /// Reads the atom:link element in the entry content. /// </summary> /// <param name="relation">The value of the rel attribute for the link element.</param> /// <param name="hrefStringValue">The value of the href attribute for the link element.</param> /// <returns>An <see cref="AtomLinkMetadata"/> instance storing the information about this link, or null if link info doesn't need to be stored.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: XmlNodeType.Element (atom:link) - the atom:link element which was read. /// </remarks> internal AtomLinkMetadata ReadAtomLinkElementInEntryContent(string relation, string hrefStringValue) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomLinkElementName, "Only atom:link element can be read by this method."); AtomLinkMetadata linkMetadata = null; if (this.ReadAtomMetadata) { linkMetadata = new AtomLinkMetadata(); linkMetadata.Relation = relation; if (this.ReadAtomMetadata) { linkMetadata.Href = hrefStringValue == null ? null : this.ProcessUriFromPayload(hrefStringValue, this.XmlReader.XmlBaseUri); } // Read the attributes while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { // Note that it's OK to store values which we don't validate in any way even if we might not need them. // The EPM reader will ignore them if they're not needed and the fact that we don't validate them means that there are no observable differences // if we store them. It keeps the code simpler (less ifs). switch (this.XmlReader.LocalName) { case AtomConstants.AtomLinkTypeAttributeName: linkMetadata.MediaType = this.XmlReader.Value; break; case AtomConstants.AtomLinkHrefLangAttributeName: linkMetadata.HrefLang = this.XmlReader.Value; break; case AtomConstants.AtomLinkTitleAttributeName: linkMetadata.Title = this.XmlReader.Value; break; case AtomConstants.AtomLinkLengthAttributeName: // We must NOT try to parse the value into a number if we don't need it for ATOM metadata. if (this.ReadAtomMetadata) { string lengthStringValue = this.XmlReader.Value; int length; if (int.TryParse(lengthStringValue, NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out length)) { linkMetadata.Length = length; } else { throw new ODataException(Strings.ODataAtomEntryMetadataDeserializer_InvalidLinkLengthValue(lengthStringValue)); } } break; default: // Ignore all other attributes. break; } } } } this.XmlReader.MoveToElement(); return(linkMetadata); }
/// <summary> /// Adds a new link to feed metadata. /// </summary> /// <param name="feedMetadata">The feed metadata to add the link to.</param> /// <param name="linkMetadata">The link metadata to add.</param> internal static void AddLink(this AtomFeedMetadata feedMetadata, AtomLinkMetadata linkMetadata) { Debug.Assert(feedMetadata != null, "feedMetadata != null"); Debug.Assert(linkMetadata != null, "linkMetadata != null"); feedMetadata.Links = feedMetadata.Links.ConcatToReadOnlyEnumerable("Ref", linkMetadata); }
/// <summary> /// Visits an ATOM link metadata. /// </summary> /// <param name="atomLinkMetadata">The link metadata to visit.</param> protected override void VisitAtomLinkMetadata(AtomLinkMetadata atomLinkMetadata) { this.ValidateUri(atomLinkMetadata.Href); base.VisitAtomLinkMetadata(atomLinkMetadata); }