예제 #1
0
        /// <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);
        }
예제 #6
0
        /// <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);
        }
예제 #8
0
        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);
            }
        }
예제 #10
0
        /// <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;
        }
예제 #11
0
        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);
        }
예제 #12
0
        /// <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;
        }
        /// <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);
            }
        }
예제 #16
0
        /// <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;
        }
예제 #20
0
        public void EntryMetadataWriterTest()
        {
            const string testPublished = "2010-10-20T20:10:00Z";
            AtomTextConstruct testRights = new AtomTextConstruct { Text = "Copyright Data Fx team." };
            AtomTextConstruct testSummary = new AtomTextConstruct { Text = "Test summary." };
            AtomTextConstruct testTitle = new AtomTextConstruct { Text = "Test title" };
            const string testUpdated = "2010-11-01T00:04:00Z";
            const string testIcon = "http://odata.org/icon";
            const string testSourceId = "http://odata.org/id/random";
            const string testLogo = "http://odata.org/logo";
            AtomTextConstruct testSubtitle = new AtomTextConstruct { Text = "Test subtitle." };

            const string testAuthorName = "Test Author 1";
            const string testAuthorEmail = "*****@*****.**";
            const string testAuthorUri = "http://odata.org/authors/1";

            var testAuthors = new AtomPersonMetadata[]
                {
                    new AtomPersonMetadata()
                    {
                        Email = testAuthorEmail,
                        Name = testAuthorName,
                        Uri = new Uri(testAuthorUri)
                    }
                };

            var testAuthors2 = new AtomPersonMetadata[0];

            const string testCategoryTerm = "Test category 1 term";
            const string testCategoryLabel = "Test category 1 label";
            const string testCategoryScheme = "http://odata.org/categories/1";

            var testCategories = new AtomCategoryMetadata[]
                {
                    new AtomCategoryMetadata()
                    {
                        Term = testCategoryTerm,
                        Label = testCategoryLabel,
                        Scheme = testCategoryScheme
                    }
                };

            const string testContributorName = "Test Contributor 1";
            const string testContributorEmail = "*****@*****.**";
            const string testContributorUri = "http://odata.org/contributors/1";

            var testContributors = new AtomPersonMetadata[]
                {
                    new AtomPersonMetadata()
                    {
                        Email = testContributorEmail,
                        Name = testContributorName,
                        Uri = new Uri(testContributorUri)
                    }
                };

            const string testGeneratorName = "Test generator";
            const string testGeneratorUri = "http://odata.org/generator";
            const string testGeneratorVersion = "3.0";

            var testGenerator = new AtomGeneratorMetadata()
                {
                    Name = testGeneratorName,
                    Uri = new Uri(testGeneratorUri),
                    Version = testGeneratorVersion
                };

            const string testLinkRelation = "http://odata.org/links/1";
            const string testLinkTitle = "Test link 1";
            const string testLinkHref = "http://odata.org/links/1";
            const string testLinkHrefLang = "de-AT";
            int? testLinkLength = 999;
            const string testLinkMediaType = "image/png";

            var testLinks = new AtomLinkMetadata[] 
                {
                    new AtomLinkMetadata()
                    {
                        Relation = testLinkRelation,
                        Title = testLinkTitle,
                        Href = new Uri(testLinkHref),
                        HrefLang = testLinkHrefLang,
                        Length = testLinkLength,
                        MediaType = testLinkMediaType
                    }
                };

            AtomFeedMetadata testSource = new AtomFeedMetadata()
            {
                Authors = testAuthors,
                Categories = testCategories,
                Contributors = testContributors,
                Generator = testGenerator,
                Icon = new Uri(testIcon),
                SourceId = new Uri(testSourceId),
                Links = testLinks,
                Logo = new Uri(testLogo),
                Rights = testRights,
                Subtitle = testSubtitle,
                Title = testTitle,
                Updated = DateTimeOffset.Parse(testUpdated)
            };

            Func<string, Func<XElement, XElement>> fragmentExtractor = (localName) => (e) => e.Element(TestAtomConstants.AtomXNamespace + localName);

            // TODO, ckerer: specify an Id via metadata if the entry does not specify one; we first have to decide what rules
            //               we want to apply to merging of metadata and ODataLib OM data.
            var testCases = new[] {
                new { // specify an author via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name>" + testAuthorName + @"</name>",
                        @"  <uri>" + testAuthorUri + @"</uri>",
                        @"  <email>" + testAuthorEmail + @"</email>",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify an empty author array via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors2),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name />",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify no authors via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = null),
                    Xml = string.Join(
                        "$(NL)",
                        @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"$(Indent)<name />",
                        @"</author>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName)
                },
                new { // specify a category via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Categories = testCategories),
                    Xml = @"<category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" xmlns=""" + TestAtomConstants.AtomNamespace + @""" />",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomCategoryElementName)
                },
                new { // specify a contributor via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Contributors = testContributors),
                    Xml = string.Join(
                        "$(NL)",
                        @"<contributor xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <name>" + testContributorName + @"</name>",
                        @"  <uri>" + testContributorUri + @"</uri>",
                        @"  <email>" + testContributorEmail + @"</email>",
                        @"</contributor>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomContributorElementName)
                },
                new { // specify a link via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Links = testLinks),
                    Xml = @"<link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @"""  xmlns=""" + TestAtomConstants.AtomNamespace + @"""/>",
                    Extractor = new Func<XElement, XElement>(
                            (e) => e.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                                    .Where(l => l.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value != TestAtomConstants.AtomSelfRelationAttributeValue)
                                    .Single())
                },
                new { // specify a published date via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Published = DateTimeOffset.Parse(testPublished)),
                    Xml = @"<published xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testPublished + @"</published>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomPublishedElementName)
                },
                new { // specify rights via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Rights = testRights),
                    Xml = @"<rights type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testRights.Text + @"</rights>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomRightsElementName)
                },
                new { // specify a source via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = testSource),
                    Xml = string.Join(
                        "$(NL)",
                        @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <id>" + testSourceId + "</id>",
                        @"  <title type=""text"">" + testTitle.Text + @"</title>",
                        @"  <subtitle type=""text"">" + testSubtitle.Text + @"</subtitle>",
                        @"  <updated>" + testUpdated + @"</updated>",
                        @"  <link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @"""/>",
                        @"  <category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" />",
                        @"  <logo>" + testLogo + @"</logo>",
                        @"  <rights type=""text"">" + testRights.Text + @"</rights>",
                        @"  <contributor>",
                        @"    <name>" + testContributorName + @"</name>",
                        @"    <uri>" + testContributorUri + @"</uri>",
                        @"    <email>" + testContributorEmail + @"</email>",
                        @"  </contributor>",
                        @"  <generator uri=""" + testGeneratorUri + @""" version=""" +testGeneratorVersion + @""">" + testGeneratorName + @"</generator>",
                        @"  <icon>" + testIcon + @"</icon>",
                        @"  <author>",
                        @"    <name>" + testAuthorName + @"</name>",
                        @"    <uri>" + testAuthorUri + @"</uri>",
                        @"    <email>" + testAuthorEmail + @"</email>",
                        @"  </author>",
                        @"</source>"),
                    Extractor = fragmentExtractor(TestAtomConstants.AtomSourceElementName)
                },
                new { // specify default feed metadata as source
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = new AtomFeedMetadata()),
                    Xml = string.Join(
                        "$(NL)",
                        @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">",
                        @"  <id />",
                        @"  <title />",
                        @"  <updated />",
                        @"</source>"),
                    Extractor = new Func<XElement, XElement>(result => {
                        var source = fragmentExtractor(TestAtomConstants.AtomSourceElementName)(result);
                        // Remove the value of updates as it can't be reliably predicted
                        source.Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomUpdatedElementName).Nodes().Remove();
                        return source;
                    })
                },
                new { // specify a summary via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Summary = testSummary),
                    Xml = @"<summary type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testSummary.Text + @"</summary>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomSummaryElementName)
                },
                new { // specify a title via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Title = testTitle),
                    Xml = @"<title type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle.Text + @"</title>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomTitleElementName)
                },
                new { // specify an updated date via metadata
                    CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Updated = DateTimeOffset.Parse(testUpdated)),
                    Xml = @"<updated xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testUpdated + @"</updated>",
                    Extractor = fragmentExtractor(TestAtomConstants.AtomUpdatedElementName)
                },
            };

            // Convert test cases to test descriptions
            IEnumerable<Func<ODataEntry>> entryCreators = new Func<ODataEntry>[]
                {
                    () => ObjectModelUtils.CreateDefaultEntry(),
                    () => ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(),
                };
            var testDescriptors = testCases.SelectMany(testCase =>
                entryCreators.Select(entryCreator =>
                {
                    ODataEntry entry = entryCreator();
                    AtomEntryMetadata metadata = entry.Atom();
                    this.Assert.IsNotNull(metadata, "Expected default entry metadata on a default entry.");
                    testCase.CustomizeMetadata(metadata);
                    return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration =>
                        new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.Xml, FragmentExtractor = testCase.Extractor });
                }));

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors.PayloadCases(WriterPayloads.EntryPayloads),
                this.WriterTestConfigurationProvider.AtomFormatConfigurations,
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger);
                });
        }
 /// <summary>
 /// Visits an ATOM 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;
        }
 /// <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*/);
 }
        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;
        }
예제 #27
0
        /// <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>
        /// 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>
        /// 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>
        /// 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);
        }
예제 #31
0
        /// <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);
 }
        /// <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();
        }