/// <summary> /// Writes the ATOM metadata for a single (resource) collection element. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="collection">The collection element to get the metadata for and write it.</param> internal static void WriteCollectionMetadata(XmlWriter writer, ODataResourceCollectionInfo collection) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(collection != null, "collection != null"); Debug.Assert(collection.Name != null, "collection.Name should have been validated at this point"); // TODO: implement writing the rest of the NYI collection metadata [see AtomPub spec]. AtomResourceCollectionMetadata metadata = collection.Atom(); string title = null; if (metadata != null) { title = metadata.Title; } if (title == null) { title = collection.Name; } // <atom:title>title</atom:title> ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title); }
/// <summary> /// Write the ATOM metadata for a feed /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="feed">The feed for which to write the metadata.</param> internal static void WriteFeedMetadata(XmlWriter writer, Uri baseUri, ODataFeed feed) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(feed != null, "feed != null"); AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>(); if (feedMetadata == null) { // create the required metadata elements with default values. // <atom:id>idValue</atom:id> Debug.Assert(!string.IsNullOrEmpty(feed.Id), "The feed Id should have been validated by now."); ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, feed.Id); // <atom:title></atom:title> ODataAtomWriterUtils.WriteEmptyElement(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace); // <atom:updated>dateTimeOffset</atom:updated> ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(DateTimeOffset.UtcNow)); } else { WriteFeedMetadata(writer, baseUri, feedMetadata, feed); } }
/// <summary> /// Creates an Xml writer over the specified stream, with the provided settings and encoding. /// </summary> /// <param name="stream">The stream to create the XmlWriter over.</param> /// <param name="odataWriterSettings">The OData writer settings used to control the settings of the Xml writer.</param> /// <param name="encoding">The encoding used for writing.</param> /// <returns>An <see cref="XmlWriter"/> instance configured with the provided settings and encoding.</returns> internal static XmlWriter CreateXmlWriter(Stream stream, ODataWriterSettings odataWriterSettings, Encoding encoding) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(stream != null, "stream != null"); Debug.Assert(odataWriterSettings != null, "odataWriterSettings != null"); XmlWriterSettings xmlWriterSettings = ODataAtomWriterUtils.CreateXmlWriterSettings(odataWriterSettings, encoding); return(XmlWriter.Create(stream, xmlWriterSettings)); }
/// <summary> /// Write an empty author element that has the required name element /// </summary> /// <param name="writer">The Xml writer to write to.</param> private static void WriteEmptyAuthor(XmlWriter writer) { // <atom:author> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); // <atom:Name></atom:Name> ODataAtomWriterUtils.WriteEmptyElement(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorNameElementName, AtomConstants.AtomNamespace); // </atom:author> writer.WriteEndElement(); }
/// <summary> /// Writes the specified start/end tags and the specified person metadata as content /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="personMetadata">The person metadata to write.</param> internal static void WritePersonMetadata(XmlWriter writer, Uri baseUri, AtomPersonMetadata personMetadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(personMetadata != null, "Person metadata must not be null."); // <atom:name>name of person</atom:name> // NOTE: write an empty element if no name is specified because the element is required. ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonNameElementName, AtomConstants.AtomNamespace, personMetadata.Name); string uriString = personMetadata.UriFromEpm; if (uriString != null) { Debug.Assert( personMetadata.Uri == null, "If the internal UriFromEpm was used, then the Uri property must be left null. The merge between custom and EPM is probably wrong."); } else { Uri uri = personMetadata.Uri; if (uri != null) { uriString = AtomUtils.ToUrlAttributeValue(uri, baseUri); } } if (uriString != null) { ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonUriElementName, AtomConstants.AtomNamespace, uriString); } string email = personMetadata.Email; if (email != null) { ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPersonEmailElementName, AtomConstants.AtomNamespace, email); } }
/// <summary> /// Constructor. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="writerSettings">Configuration settings for the writer to create.</param> /// <param name="encoding">The encoding to use for writing.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> internal ODataAtomCollectionWriter( Stream stream, ODataWriterSettings writerSettings, Encoding encoding, DataServiceMetadataProviderWrapper metadataProvider, bool synchronous) : base(writerSettings.Version, metadataProvider, synchronous) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(stream != null, "stream != null"); Debug.Assert(writerSettings != null, "writerSettings != null"); this.outputStream = new AsyncBufferedStream(stream); this.writer = ODataAtomWriterUtils.CreateXmlWriter(this.outputStream, writerSettings, encoding); }
/// <summary> /// Start writing a feed. /// </summary> /// <param name="feed">The feed to write.</param> protected override void StartFeed(ODataFeed feed) { Debug.Assert(feed != null, "feed != null"); // <atom:feed> this.writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomFeedElementName, AtomConstants.AtomNamespace); if (this.IsTopLevel) { ODataAtomWriterUtils.WriteBaseUriAndDefaultNamespaceAttributes(this.writer, this.BaseUri); if (feed.Count.HasValue) { ODataAtomWriterUtils.WriteCount(this.writer, feed.Count.Value, false); } } }
/// <summary> /// Start writing an entry. /// </summary> /// <param name="entry">The entry to write.</param> protected override void StartEntry(ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); // <entry> this.writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomEntryElementName, AtomConstants.AtomNamespace); if (this.IsTopLevel) { ODataAtomWriterUtils.WriteBaseUriAndDefaultNamespaceAttributes(this.writer, this.BaseUri); } string etag = entry.ETag; if (etag != null) { // TODO, ckerer: if this is a top-level entry also put the ETag into the headers. ODataAtomWriterUtils.WriteETag(this.writer, etag); } }
/// <summary> /// Writes the ATOM metadata for a single workspace element. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="workspace">The workspace element to get the metadata for and write it.</param> internal static void WriteWorkspaceMetadata(XmlWriter writer, ODataWorkspace workspace) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); Debug.Assert(workspace != null, "workspace != null"); AtomWorkspaceMetadata metadata = workspace.Atom(); string title = null; if (metadata != null) { title = metadata.Title; } if (title == null) { title = AtomConstants.AtomWorkspaceDefaultTitle; } // <atom:title>title</atom:title> ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title); }
/// <summary> /// Constructor creating an OData writer using the ATOM format. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="odataWriterSettings">Configuration settings for the writer to create.</param> /// <param name="encoding">The encoding to use for writing.</param> /// <param name="writingResponse">True if the writer is to write a response payload; false if it's to write a request payload.</param> /// <param name="metadataProvider">The metadata provider to use.</param> /// <param name="writingFeed">True if the writer is created for writing a feed; false when it is created for writing an entry.</param> /// <param name="synchronous">True if the writer is created for synchronous operation; false for asynchronous.</param> internal ODataAtomWriter( Stream stream, ODataWriterSettings odataWriterSettings, Encoding encoding, bool writingResponse, DataServiceMetadataProviderWrapper metadataProvider, bool writingFeed, bool synchronous) : base( odataWriterSettings.Version, odataWriterSettings.BaseUri, writingResponse, metadataProvider, writingFeed, synchronous) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(stream != null, "stream != null"); Debug.Assert(odataWriterSettings != null, "odataWriterSettings != null"); this.outputStream = new AsyncBufferedStream(stream); this.writer = ODataAtomWriterUtils.CreateXmlWriter(this.outputStream, odataWriterSettings, encoding); }
/// <summary> /// Writes a collection item (either primitive or complex) /// </summary> /// <param name="item">The collection item to write.</param> protected override void WriteItemImplementation(object item) { // <d:element> this.writer.WriteStartElement(AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataNamespace); if (item == null) { // NOTE can't use ODataAtomWriterUtils.WriteNullAttribute because that method assumes the // default 'm' prefix for the metadata namespace. this.writer.WriteAttributeString( AtomConstants.ODataNullAttributeName, AtomConstants.ODataMetadataNamespace, AtomConstants.AtomTrueLiteral); } else { ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { ODataAtomWriterUtils.WriteComplexValue(this.writer, this.MetadataProvider, complexValue, null, false, true, this.Version, null, null); } else { ODataMultiValue multiValue = item as ODataMultiValue; if (multiValue != null) { throw new ODataException(Strings.ODataCollectionWriter_MultiValuesNotSupportedInCollections); } ODataAtomWriterUtils.WritePrimitiveValue(this.writer, item, null); } } // </d:element> this.writer.WriteEndElement(); }
/// <summary> /// Write the given feed metadata in atom format /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="feedMetadata">The metadata to write.</param> /// <param name="feed">The feed for which to write the meadata or null if it is the metadata of an atom:source element.</param> private static void WriteFeedMetadata(XmlWriter writer, Uri baseUri, AtomFeedMetadata feedMetadata, ODataFeed feed) { Debug.Assert(writer != null, "writer != null"); 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 Debug.Assert(feed == null || !string.IsNullOrEmpty(feed.Id), "The feed Id should have been validated by now."); string id = feed == null ? feedMetadata.SourceId : feed.Id; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, id); // <atom:title>text</atom:title> // NOTE: write an empty element if no title is specified since the element is required ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title); if (feedMetadata.Subtitle != null) { // <atom:subtitle>text</atom:subtitle> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. DateTimeOffset updated = feedMetadata.Updated.HasValue ? feedMetadata.Updated.Value : DateTimeOffset.UtcNow; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(updated)); IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { // <atom:author>author data</atom:author> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, author); writer.WriteEndElement(); } } IEnumerable <AtomLinkMetadata> links = feedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { // <atom:link>...</atom:link> WriteAtomLinkMetadata(writer, baseUri, link); } } IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { // <atom:category term="..." scheme="..." label="..."></atom:category> WriteCategory(writer, category); } } Uri logo = feedMetadata.Logo; if (logo != null) { // <atom:logo>Uri</atom:logo> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLogoElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(logo, baseUri)); } if (feedMetadata.Rights != null) { // <atom:rights>rights</atom:rights> ODataAtomWriterMetadataUtils.WriteTextConstruct( writer, 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> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, contributor); writer.WriteEndElement(); } } AtomGeneratorMetadata generator = feedMetadata.Generator; if (generator != null) { // <atom:generator uri="..." version="...">name</atom:generator> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace); if (generator.Uri != null) { writer.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, AtomUtils.ToUrlAttributeValue(generator.Uri, baseUri)); } if (!string.IsNullOrEmpty(generator.Version)) { writer.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version); } writer.WriteString(generator.Name); writer.WriteEndElement(); } Uri icon = feedMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(icon, baseUri)); } }
/// <summary> /// Finish writing an entry. /// </summary> /// <param name="entry">The entry to write.</param> protected override void EndEntry(ODataEntry entry) { Debug.Assert(entry != null, "entry != null"); string typeName = entry.TypeName; ResourceType entryType = ValidationUtils.ValidateTypeName(this.MetadataProvider, typeName, ResourceTypeKind.EntityType, false); // Initialize the property value cache and cache the entry properties. EntryPropertiesValueCache propertyValueCache = new EntryPropertiesValueCache(entry); EpmResourceTypeAnnotation epmResourceTypeAnnotation = null; if (entryType != null) { Debug.Assert(entryType.IsReadOnly, "The resource must be read-only to be applied to an entry."); entryType.EnsureEpmAvailability(); epmResourceTypeAnnotation = entryType.Epm(); } // <atom:id>idValue</atom:id> // NOTE: do not generate a relative Uri for the ID; it is independent of xml:base ODataAtomWriterUtils.WriteElementWithTextContent( this.writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, entry.Id); // <category term="type" scheme="odatascheme"/> // If no type information is provided, don't include the category element for type at all // NOTE: the validation of the type name happened at the beginning of this method. if (typeName != null) { ODataAtomWriterMetadataUtils.WriteCategory(this.writer, typeName, AtomConstants.ODataSchemeNamespace, null); } Uri editLink = entry.EditLink; if (editLink != null) { // <link rel="edit" title="Title" href="LinkHRef"/> ODataAtomWriterUtils.WriteReadOrEditLink(this.writer, this.BaseUri, editLink, AtomConstants.AtomEditRelationAttributeValue, typeName); } Uri readLink = entry.ReadLink; if (readLink != null) { // <link rel="self" title="Title" href="LinkHRef"/> ODataAtomWriterUtils.WriteReadOrEditLink(this.writer, this.BaseUri, readLink, AtomConstants.AtomSelfRelationAttributeValue, typeName); } // named streams IEnumerable <ODataMediaResource> namedStreams = entry.NamedStreams; if (namedStreams != null) { foreach (ODataMediaResource namedStream in namedStreams) { ValidationUtils.ValidateNamedStream(namedStream, this.Version); ODataAtomWriterUtils.WriteNamedStream(this.writer, this.BaseUri, namedStream); } } // association links IEnumerable <ODataAssociationLink> associationLinks = entry.AssociationLinks; if (associationLinks != null) { foreach (ODataAssociationLink associationLink in associationLinks) { ValidationUtils.ValidateAssociationLink(associationLink, this.Version); ODataAtomWriterUtils.WriteAssociationLink(this.writer, this.BaseUri, entry, associationLink); } } // write entry metadata including syndication EPM AtomEntryMetadata epmEntryMetadata = null; if (epmResourceTypeAnnotation != null) { ODataVersionChecker.CheckEntityPropertyMapping(this.Version, entryType); epmEntryMetadata = EpmSyndicationWriter.WriteEntryEpm( epmResourceTypeAnnotation.EpmTargetTree, propertyValueCache, entryType, this.MetadataProvider, this.Version); } ODataAtomWriterMetadataUtils.WriteEntryMetadata(this.writer, this.BaseUri, entry, epmEntryMetadata); // write the content this.WriteEntryContent(entry, entryType, propertyValueCache, epmResourceTypeAnnotation == null ? null : epmResourceTypeAnnotation.EpmSourceTree.Root); // write custom EPM if (epmResourceTypeAnnotation != null) { EpmCustomWriter.WriteEntryEpm( this.writer, epmResourceTypeAnnotation.EpmTargetTree, propertyValueCache, entryType, this.MetadataProvider); } // </entry> this.writer.WriteEndElement(); }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="property">The property to write out.</param> /// <param name="owningType">The type owning the property (or null if no metadata is available).</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="isTopLevel">True if writing a top-level property payload; otherwise false.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param> internal static void WriteProperty( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataProperty property, ResourceType owningType, ODataVersion version, bool isTopLevel, bool isWritingCollection, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); ValidationUtils.ValidateProperty(property); ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType); EpmSourcePathSegment epmSourcePathSegment = null; if (epmParentSourcePathSegment != null) { epmSourcePathSegment = epmParentSourcePathSegment.SubProperties.Where(subProperty => subProperty.PropertyName == property.Name).FirstOrDefault(); } object value = property.Value; // TODO: If we implement validation or type conversions the value needs to be converted here // since the next method call needs to know if the value is a string or not in some cases. // If EPM tells us to skip this property in content, then we're done here. if (!ShouldWritePropertyInContent(value, epmSourcePathSegment, version)) { return; } // <d:propertyname> writer.WriteStartElement( isWritingCollection ? string.Empty : AtomConstants.ODataNamespacePrefix, property.Name, AtomConstants.ODataNamespace); if (isTopLevel) { WriteDefaultNamespaceAttributes(writer, DefaultNamespaceFlags.OData | DefaultNamespaceFlags.ODataMetadata); } // Null property value. if (value == null) { // verify that MultiValue properties are not null if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue) { throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name)); } ODataAtomWriterUtils.WriteNullAttribute(writer); } else { ODataComplexValue complexValue = value as ODataComplexValue; ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType; bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null; // Complex properties are written recursively. if (complexValue != null) { WriteComplexValue(writer, metadata, complexValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { ODataVersionChecker.CheckMultiValueProperties(version, property.Name); WriteMultiValue(writer, metadata, multiValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { WritePrimitiveValue(writer, value, resourcePropertyType); } } } // </d:propertyname> writer.WriteEndElement(); }
/// <summary> /// Write the content of the given entry. /// </summary> /// <param name="entry">The entry for which to write properties.</param> /// <param name="entryType">The <see cref="ResourceType"/> of the entry (or null if not metadata is available).</param> /// <param name="propertiesValueCache">The cache of properties.</param> /// <param name="rootSourcePathSegment">The root of the EPM source tree, if there's an EPM applied.</param> private void WriteEntryContent(ODataEntry entry, ResourceType entryType, EntryPropertiesValueCache propertiesValueCache, EpmSourcePathSegment rootSourcePathSegment) { Debug.Assert(entry != null, "entry != null"); Debug.Assert(propertiesValueCache != null, "propertiesValueCache != null"); ODataMediaResource mediaResource = entry.MediaResource; if (mediaResource == null) { // <content type="application/xml"> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContentElementName, AtomConstants.AtomNamespace); this.writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, MimeConstants.MimeApplicationXml); // <m:properties> // we always write the <m:properties> element even if there are no properties this.writer.WriteStartElement( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.AtomPropertiesElementName, AtomConstants.ODataMetadataNamespace); ODataAtomWriterUtils.WriteProperties( this.writer, this.MetadataProvider, entryType, propertiesValueCache.EntryProperties, this.Version, false, propertiesValueCache, rootSourcePathSegment); // </m:properties> this.writer.WriteEndElement(); // </content> this.writer.WriteEndElement(); } else { Uri mediaEditLink = mediaResource.EditLink; if (mediaEditLink != null) { // <link rel="edit-media" href="href" /> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLinkElementName, AtomConstants.AtomNamespace); this.writer.WriteAttributeString( AtomConstants.AtomLinkRelationAttributeName, AtomConstants.AtomEditMediaRelationAttributeValue); this.writer.WriteAttributeString( AtomConstants.AtomHRefAttributeName, AtomUtils.ToUrlAttributeValue(mediaEditLink, this.BaseUri)); string mediaETag = mediaResource.ETag; if (mediaETag != null) { this.writer.WriteAttributeString( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataETagAttributeName, AtomConstants.ODataMetadataNamespace, mediaETag); } // </link> this.writer.WriteEndElement(); } Debug.Assert(mediaEditLink != null || mediaResource.ETag == null, "The default stream edit link and etag should have been validated by now."); // <content type="type" src="src"> this.writer.WriteStartElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContentElementName, AtomConstants.AtomNamespace); Debug.Assert(!string.IsNullOrEmpty(mediaResource.ContentType), "The default stream content type should have been validated by now."); this.writer.WriteAttributeString( AtomConstants.AtomTypeAttributeName, mediaResource.ContentType); Debug.Assert(mediaResource.ReadLink != null, "The default stream read link should have been validated by now."); this.writer.WriteAttributeString( AtomConstants.MediaLinkEntryContentSourceAttributeName, AtomUtils.ToUrlAttributeValue(mediaResource.ReadLink, this.BaseUri)); // </content> this.writer.WriteEndElement(); // <m:properties> // we always write the <m:properties> element even if there are no properties this.writer.WriteStartElement( AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.AtomPropertiesElementName, AtomConstants.ODataMetadataNamespace); ODataAtomWriterUtils.WriteProperties( this.writer, this.MetadataProvider, entryType, propertiesValueCache.EntryProperties, this.Version, false, propertiesValueCache, rootSourcePathSegment); // </m:properties> this.writer.WriteEndElement(); } }
/// <summary> /// Write the ATOM metadata for an entry /// </summary> /// <param name="writer">The Xml writer to write to.</param> /// <param name="baseUri">The base Uri of the document or null if none was specified.</param> /// <param name="entry">The entry for which to write the metadata.</param> /// <param name="epmEntryMetadata">The ATOM metadata for the entry which came from EPM.</param> internal static void WriteEntryMetadata(XmlWriter writer, Uri baseUri, ODataEntry entry, AtomEntryMetadata epmEntryMetadata) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); // TODO, ckerer: implement the rule around authors (an entry has to have an author directly or in the <entry:source> unless the feed has an author). // currently we make all entries have an author. AtomEntryMetadata customEntryMetadata = entry.GetAnnotation <AtomEntryMetadata>(); AtomEntryMetadata entryMetadata = ODataAtomWriterMetadataEpmMergeUtils.MergeCustomAndEpmEntryMetadata(customEntryMetadata, epmEntryMetadata); if (entryMetadata == null) { // write all required metadata elements with default content // <atom:title></atom:title> ODataAtomWriterUtils.WriteEmptyElement(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace); // <atom:updated>dateTimeOffset</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(DateTimeOffset.UtcNow)); WriteEmptyAuthor(writer); } else { // <atom:title>text</atom:title> // NOTE: writes an empty element even if no title was specified since the title is required ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, entryMetadata.Title); AtomTextConstruct summary = entryMetadata.Summary; if (summary != null) { // <atom:summary>text</atom:summary> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSummaryElementName, AtomConstants.AtomNamespace, summary); } DateTimeOffset?published = entryMetadata.Published; if (published.HasValue) { // <atom:published>dateTimeOffset</atom:published> ODataAtomWriterUtils.WriteElementWithTextContent(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPublishedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(published.Value)); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified the best we can do is to create a default // one with the current date/time. DateTimeOffset updated = entryMetadata.Updated.HasValue ? entryMetadata.Updated.Value : DateTimeOffset.UtcNow; ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, ODataAtomConvert.ToString(updated)); bool wroteAuthor = false; IEnumerable <AtomPersonMetadata> authors = entryMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { if (author == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_AuthorMetadataMustNotContainNull); } // <atom:author>author data</atom:author> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, author); writer.WriteEndElement(); wroteAuthor = true; } } if (!wroteAuthor) { // write empty authors since they are required WriteEmptyAuthor(writer); } IEnumerable <AtomPersonMetadata> contributors = entryMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { if (contributor == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_ContributorMetadataMustNotContainNull); } // <atom:contributor>contributor data</atom:contributor> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); WritePersonMetadata(writer, baseUri, contributor); writer.WriteEndElement(); } } IEnumerable <AtomLinkMetadata> links = entryMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { if (link == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_LinkMetadataMustNotContainNull); } // <atom:link>...</atom:link> WriteAtomLinkMetadata(writer, baseUri, link); } } IEnumerable <AtomCategoryMetadata> categories = entryMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { if (category == null) { throw new ODataException(Strings.ODataAtomWriterMetadataUtils_CategoryMetadataMustNotContainNull); } // <atom:category term="..." scheme="..." label="..."></atom:category> WriteCategory(writer, category); } } if (entryMetadata.Rights != null) { // <atom:rights>rights</atom:rights> ODataAtomWriterMetadataUtils.WriteTextConstruct(writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, entryMetadata.Rights); } Uri icon = entryMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> ODataAtomWriterUtils.WriteElementWithTextContent( writer, AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, AtomUtils.ToUrlAttributeValue(icon, baseUri)); } AtomFeedMetadata source = entryMetadata.Source; if (source != null) { // <atom:source> writer.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSourceElementName, AtomConstants.AtomNamespace); WriteFeedMetadata(writer, baseUri, source, null); // </atom:source> writer.WriteEndElement(); } } }
/// <summary> /// Write an error message. /// </summary> /// <param name='error'>The error to write.</param> /// <param name="includeDebugInformation">If in debug mode error details will be included (if present).</param> protected override void SerializeError(ODataError error, bool includeDebugInformation) { Debug.Assert(error != null, "error != null"); ODataAtomWriterUtils.WriteError(this.writer, error, includeDebugInformation); }