Example #1
0
        /// <summary>
        /// Checks if mappings could potentially result in mixed content and dis-allows it.
        /// </summary>
        /// <param name="currentSegment">Segment being processed.</param>
        /// <param name="ancestorsWithContent">A list of ancestor attributes that have content.
        /// Can contain a maximum of one attribute when the method is called, must never contain more than two.</param>
        /// <returns>boolean indicating if the tree is valid or not.</returns>
        private static bool HasMixedContent(EpmTargetPathSegment currentSegment, List <EntityPropertyMappingAttribute> ancestorsWithContent)
        {
            Debug.Assert(ancestorsWithContent != null && ancestorsWithContent.Count < 2, "Expected at most 1 ancestor with content.");

            foreach (EpmTargetPathSegment childSegment in currentSegment.SubSegments.Where(s => !s.IsAttribute))
            {
                if (childSegment.HasContent && ancestorsWithContent.Count == 1)
                {
                    ancestorsWithContent.Add(childSegment.EpmInfo.Attribute);
                    return(true);
                }

                if (childSegment.HasContent)
                {
                    Debug.Assert(ancestorsWithContent.Count == 0, "Should have already returned if we had ancestors with content.");
                    ancestorsWithContent.Add(childSegment.EpmInfo.Attribute);
                }

                if (HasMixedContent(childSegment, ancestorsWithContent))
                {
                    Debug.Assert(ancestorsWithContent.Count == 2, "Must have two attributes with content to return true.");
                    return(true);
                }

                if (childSegment.HasContent)
                {
                    Debug.Assert(ancestorsWithContent.Count == 1, "Should have the one item in the list that we added before.");
                    ancestorsWithContent.Clear();
                }
            }

            return(false);
        }
Example #2
0
 private static bool HasMixedContent(EpmTargetPathSegment currentSegment, List <EntityPropertyMappingAttribute> ancestorsWithContent)
 {
     foreach (EpmTargetPathSegment segment in from s in currentSegment.SubSegments
              where !s.IsAttribute
              select s)
     {
         if (segment.HasContent && (ancestorsWithContent.Count == 1))
         {
             ancestorsWithContent.Add(segment.EpmInfo.Attribute);
             return(true);
         }
         if (segment.HasContent)
         {
             ancestorsWithContent.Add(segment.EpmInfo.Attribute);
         }
         if (HasMixedContent(segment, ancestorsWithContent))
         {
             return(true);
         }
         if (segment.HasContent)
         {
             ancestorsWithContent.Clear();
         }
     }
     return(false);
 }
Example #3
0
 private void WriteElementEpm(XmlWriter writer, EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType, ref string alreadyDeclaredPrefix)
 {
     string prefix = targetSegment.SegmentNamespacePrefix ?? string.Empty;
     writer.WriteStartElement(prefix, targetSegment.SegmentName, targetSegment.SegmentNamespaceUri);
     if (prefix.Length > 0)
     {
         WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix);
     }
     foreach (EpmTargetPathSegment segment in targetSegment.SubSegments)
     {
         if (segment.IsAttribute)
         {
             this.WriteAttributeEpm(writer, segment, epmValueCache, entityType, ref alreadyDeclaredPrefix);
         }
     }
     if (targetSegment.HasContent)
     {
         string str2 = this.GetEntryPropertyValueAsText(targetSegment, epmValueCache, entityType);
         ODataAtomWriterUtils.WriteString(writer, str2);
     }
     else
     {
         foreach (EpmTargetPathSegment segment2 in targetSegment.SubSegments)
         {
             if (!segment2.IsAttribute)
             {
                 this.WriteElementEpm(writer, segment2, epmValueCache, entityType, ref alreadyDeclaredPrefix);
             }
         }
     }
     writer.WriteEndElement();
 }
        protected AtomPersonMetadata ReadAtomPersonConstruct(EpmTargetPathSegment epmTargetPathSegment)
        {
            AtomPersonMetadata metadata = new AtomPersonMetadata();
            if (base.XmlReader.IsEmptyElement)
            {
                goto Label_011B;
            }
            base.XmlReader.Read();
        Label_0022:
            switch (base.XmlReader.NodeType)
            {
                case XmlNodeType.Element:
                    EpmTargetPathSegment segment;
                    string str2;
                    if ((base.XmlReader.NamespaceEquals(this.AtomNamespace) && this.ShouldReadElement(epmTargetPathSegment, base.XmlReader.LocalName, out segment)) && ((str2 = base.XmlReader.LocalName) != null))
                    {
                        if (!(str2 == "name"))
                        {
                            if (str2 == "uri")
                            {
                                Uri xmlBaseUri = base.XmlReader.XmlBaseUri;
                                string uriFromPayload = this.ReadElementStringValue();
                                if (segment != null)
                                {
                                    metadata.UriFromEpm = uriFromPayload;
                                }
                                if (this.ReadAtomMetadata)
                                {
                                    metadata.Uri = base.ProcessUriFromPayload(uriFromPayload, xmlBaseUri);
                                }
                                goto Label_0109;
                            }
                            if (str2 == "email")
                            {
                                metadata.Email = this.ReadElementStringValue();
                                goto Label_0109;
                            }
                        }
                        else
                        {
                            metadata.Name = this.ReadElementStringValue();
                            goto Label_0109;
                        }
                    }
                    break;

                case XmlNodeType.EndElement:
                    goto Label_0109;
            }
            base.XmlReader.Skip();
        Label_0109:
            if (base.XmlReader.NodeType != XmlNodeType.EndElement)
            {
                goto Label_0022;
            }
        Label_011B:
            base.XmlReader.Read();
            return metadata;
        }
Example #5
0
 private static void WriteNamespaceDeclaration(XmlWriter writer, EpmTargetPathSegment targetSegment, ref string alreadyDeclaredPrefix)
 {
     if (alreadyDeclaredPrefix == null)
     {
         writer.WriteAttributeString("xmlns", targetSegment.SegmentNamespacePrefix, "http://www.w3.org/2000/xmlns/", targetSegment.SegmentNamespaceUri);
         alreadyDeclaredPrefix = targetSegment.SegmentNamespacePrefix;
     }
 }
Example #6
0
 private string GetEntryPropertyValueAsText(EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType)
 {
     object propertyValue = base.ReadEntryPropertyValue(targetSegment.EpmInfo, epmValueCache, entityType);
     if (propertyValue == null)
     {
         return string.Empty;
     }
     return EpmWriterUtils.GetPropertyValueAsText(propertyValue);
 }
 private void ReadCustomEpmAttribute(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegmentForElement)
 {
     string localName = base.XmlReader.LocalName;
     string namespaceUri = base.XmlReader.NamespaceURI;
     EpmTargetPathSegment segment = epmTargetPathSegmentForElement.SubSegments.FirstOrDefault<EpmTargetPathSegment>(x => (x.IsAttribute && (string.CompareOrdinal(x.AttributeName, localName) == 0)) && (string.CompareOrdinal(x.SegmentNamespaceUri, namespaceUri) == 0));
     if ((segment != null) && !entryState.EpmCustomReaderValueCache.Contains(segment.EpmInfo))
     {
         entryState.EpmCustomReaderValueCache.Add(segment.EpmInfo, base.XmlReader.Value);
     }
 }
Example #8
0
 private void WriteAttributeEpm(XmlWriter writer, EpmTargetPathSegment targetSegment, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType, ref string alreadyDeclaredPrefix)
 {
     string str = this.GetEntryPropertyValueAsText(targetSegment, epmValueCache, entityType);
     string prefix = targetSegment.SegmentNamespacePrefix ?? string.Empty;
     writer.WriteAttributeString(prefix, targetSegment.AttributeName, targetSegment.SegmentNamespaceUri, str);
     if (prefix.Length > 0)
     {
         WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix);
     }
 }
 internal EpmTargetPathSegment(string segmentName, string segmentNamespaceUri, string segmentNamespacePrefix, EpmTargetPathSegment parentSegment) : this()
 {
     this.segmentName            = segmentName;
     this.segmentNamespaceUri    = segmentNamespaceUri;
     this.segmentNamespacePrefix = segmentNamespacePrefix;
     this.parentSegment          = parentSegment;
     if (!string.IsNullOrEmpty(segmentName) && (segmentName[0] == '@'))
     {
         this.segmentAttributeName = segmentName.Substring(1);
     }
 }
Example #10
0
 internal EpmTargetPathSegment(string segmentName, string segmentNamespaceUri, string segmentNamespacePrefix, EpmTargetPathSegment parentSegment) : this()
 {
     this.segmentName = segmentName;
     this.segmentNamespaceUri = segmentNamespaceUri;
     this.segmentNamespacePrefix = segmentNamespacePrefix;
     this.parentSegment = parentSegment;
     if (!string.IsNullOrEmpty(segmentName) && (segmentName[0] == '@'))
     {
         this.segmentAttributeName = segmentName.Substring(1);
     }
 }
Example #11
0
        /// <summary>
        /// Removes a path in the tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/>.
        /// </summary>
        /// <param name="epmInfo">EnitityPropertyMappingInfo holding the target path</param>
        internal void Remove(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(epmInfo != null, "epmInfo != null");

            string targetName   = epmInfo.Attribute.TargetPath;
            string namespaceUri = epmInfo.Attribute.TargetNamespaceUri;

            EpmTargetPathSegment        currentSegment    = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot;
            List <EpmTargetPathSegment> activeSubSegments = currentSegment.SubSegments;

            Debug.Assert(!string.IsNullOrEmpty(targetName), "Must have been validated during EntityPropertyMappingAttribute construction");
            string[] targetSegments = targetName.Split('/');
            for (int i = 0; i < targetSegments.Length; i++)
            {
                string targetSegment = targetSegments[i];

                Debug.Assert(targetSegment.Length > 0 && (targetSegment[0] != '@' || i == targetSegments.Length - 1), "Target segments should have been checked when adding the path to the tree");

                EpmTargetPathSegment foundSegment = activeSubSegments.FirstOrDefault(
                    segment => segment.SegmentName == targetSegment &&
                    (epmInfo.IsSyndicationMapping || segment.SegmentNamespaceUri == namespaceUri));
                if (foundSegment != null)
                {
                    currentSegment = foundSegment;
                }
                else
                {
                    return;
                }

                activeSubSegments = currentSegment.SubSegments;
            }

            // Recursively remove all the parent segments which will have no more children left
            // after removal of the current segment node
            if (currentSegment.EpmInfo != null)
            {
                // Since we are removing a property with KeepInContent false, we should decrement the count
                if (!currentSegment.EpmInfo.Attribute.KeepInContent)
                {
                    this.countOfNonContentV2Mappings--;
                }

                EpmTargetPathSegment parentSegment = null;
                do
                {
                    parentSegment = currentSegment.ParentSegment;
                    parentSegment.SubSegments.Remove(currentSegment);
                    currentSegment = parentSegment;
                }while (currentSegment.ParentSegment != null && !currentSegment.HasContent && currentSegment.SubSegments.Count == 0);
            }
        }
Example #12
0
        /// <summary>
        /// Checks the validity of a tree.
        /// </summary>
        /// <param name="currentSegment">The segment to validate.</param>
        private static void DebugValidate(EpmTargetPathSegment currentSegment)
        {
            if (currentSegment.ParentSegment != null)
            {
                Debug.Assert(!currentSegment.ParentSegment.IsAttribute, "Attributes must be leaf nodes.");
            }

            // Walk recursively subsegments and validate them
            foreach (EpmTargetPathSegment subSegment in currentSegment.SubSegments)
            {
                DebugValidate(subSegment);
            }
        }
Example #13
0
 private bool TryReadCustomEpmElement(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegment)
 {
     string localName = base.XmlReader.LocalName;
     string namespaceUri = base.XmlReader.NamespaceURI;
     EpmTargetPathSegment epmTargetPathSegmentForElement = epmTargetPathSegment.SubSegments.FirstOrDefault<EpmTargetPathSegment>(segment => (!segment.IsAttribute && (string.CompareOrdinal(segment.SegmentName, localName) == 0)) && (string.CompareOrdinal(segment.SegmentNamespaceUri, namespaceUri) == 0));
     if ((epmTargetPathSegmentForElement != null) && (!epmTargetPathSegmentForElement.HasContent || !entryState.EpmCustomReaderValueCache.Contains(epmTargetPathSegmentForElement.EpmInfo)))
     {
         while (base.XmlReader.MoveToNextAttribute())
         {
             this.ReadCustomEpmAttribute(entryState, epmTargetPathSegmentForElement);
         }
         base.XmlReader.MoveToElement();
         if (epmTargetPathSegmentForElement.HasContent)
         {
             string str = base.ReadElementStringValue();
             entryState.EpmCustomReaderValueCache.Add(epmTargetPathSegmentForElement.EpmInfo, str);
             goto Label_0115;
         }
         if (!base.XmlReader.IsEmptyElement)
         {
             base.XmlReader.Read();
             while (base.XmlReader.NodeType != XmlNodeType.EndElement)
             {
                 switch (base.XmlReader.NodeType)
                 {
                     case XmlNodeType.Element:
                     {
                         if (!this.TryReadCustomEpmElement(entryState, epmTargetPathSegmentForElement))
                         {
                             base.XmlReader.Skip();
                         }
                         continue;
                     }
                     case XmlNodeType.EndElement:
                     {
                         continue;
                     }
                 }
                 base.XmlReader.Skip();
             }
         }
     }
     else
     {
         return false;
     }
     base.XmlReader.Read();
 Label_0115:
     return true;
 }
        /// <summary>
        /// Used for creating non-root nodes in the syndication/custom trees.
        /// </summary>
        /// <param name="segmentName">Name of xml element/attribute</param>
        /// <param name="segmentNamespaceUri">URI of the namespace for <paramref name="segmentName"/></param>
        /// <param name="segmentNamespacePrefix">Namespace prefix to be used for <paramref name="segmentNamespaceUri"/></param>
        /// <param name="parentSegment">Reference to the parent node if this is a sub-node, useful for traversals in visitors</param>
        internal EpmTargetPathSegment(string segmentName, string segmentNamespaceUri, string segmentNamespacePrefix, EpmTargetPathSegment parentSegment)
            : this()
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(segmentName == null || segmentName.Length > 0, "Empty segment name is not allowed.");

            this.segmentName            = segmentName;
            this.segmentNamespaceUri    = segmentNamespaceUri;
            this.segmentNamespacePrefix = segmentNamespacePrefix;
            this.parentSegment          = parentSegment;

            if (!string.IsNullOrEmpty(segmentName) && segmentName[0] == '@')
            {
                this.segmentAttributeName = segmentName.Substring(1);
            }
        }
Example #15
0
        internal void Remove(EntityPropertyMappingInfo epmInfo)
        {
            string targetPath   = epmInfo.Attribute.TargetPath;
            string namespaceUri = epmInfo.Attribute.TargetNamespaceUri;
            EpmTargetPathSegment        item        = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot;
            List <EpmTargetPathSegment> subSegments = item.SubSegments;

            string[] strArray = targetPath.Split(new char[] { '/' });
            for (int i = 0; i < strArray.Length; i++)
            {
                string targetSegment          = strArray[i];
                EpmTargetPathSegment segment2 = subSegments.FirstOrDefault <EpmTargetPathSegment>(delegate(EpmTargetPathSegment segment) {
                    if (!(segment.SegmentName == targetSegment))
                    {
                        return(false);
                    }
                    if (!epmInfo.IsSyndicationMapping)
                    {
                        return(segment.SegmentNamespaceUri == namespaceUri);
                    }
                    return(true);
                });
                if (segment2 != null)
                {
                    item = segment2;
                }
                else
                {
                    return;
                }
                subSegments = item.SubSegments;
            }
            if (item.EpmInfo != null)
            {
                if (!item.EpmInfo.Attribute.KeepInContent)
                {
                    this.countOfNonContentV2Mappings--;
                }
                EpmTargetPathSegment parentSegment = null;
                do
                {
                    parentSegment = item.ParentSegment;
                    parentSegment.SubSegments.Remove(item);
                    item = parentSegment;
                }while (((item.ParentSegment != null) && !item.HasContent) && (item.SubSegments.Count == 0));
            }
        }
Example #16
0
        /// <summary>
        /// Writes a namespace declaration attribute for the namespace required by the target segment.
        /// </summary>
        /// <param name="writer">The writer to write the declaration to.</param>
        /// <param name="targetSegment">The target segment to write the declaration for.</param>
        /// <param name="alreadyDeclaredPrefix">The name of the prefix if it was already declared.</param>
        private static void WriteNamespaceDeclaration(
            XmlWriter writer,
            EpmTargetPathSegment targetSegment,
            ref string alreadyDeclaredPrefix)
        {
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(targetSegment != null, "targetSegment != null");

            Debug.Assert(
                alreadyDeclaredPrefix == null || alreadyDeclaredPrefix == targetSegment.SegmentNamespacePrefix,
                "Found a subsegment with different prefix than the parent segment. The custom EPM writer is not ready to handle that.");

            if (alreadyDeclaredPrefix == null)
            {
                writer.WriteAttributeString(
                    AtomConstants.XmlnsNamespacePrefix,
                    targetSegment.SegmentNamespacePrefix,
                    AtomConstants.XmlNamespacesNamespace,
                    targetSegment.SegmentNamespaceUri);

                alreadyDeclaredPrefix = targetSegment.SegmentNamespacePrefix;
            }
        }
Example #17
0
 /// <summary>
 /// Initializes the sub-trees for syndication and non-syndication content.
 /// </summary>
 internal EpmTargetTree()
 {
     DebugUtils.CheckNoExternalCallers();
     this.syndicationRoot    = new EpmTargetPathSegment();
     this.nonSyndicationRoot = new EpmTargetPathSegment();
 }
Example #18
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(epmInfo != null, "epmInfo != null");

            string targetPath      = epmInfo.Attribute.TargetPath;
            string namespaceUri    = epmInfo.Attribute.TargetNamespaceUri;
            string namespacePrefix = epmInfo.Attribute.TargetNamespacePrefix;

            EpmTargetPathSegment         currentSegment    = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot;
            IList <EpmTargetPathSegment> activeSubSegments = currentSegment.SubSegments;

            Debug.Assert(!string.IsNullOrEmpty(targetPath), "Must have been validated during EntityPropertyMappingAttribute construction");
            string[] targetSegments = targetPath.Split('/');

            EpmTargetPathSegment foundSegment = null;

            for (int i = 0; i < targetSegments.Length; i++)
            {
                string targetSegment = targetSegments[i];

                if (targetSegment.Length == 0)
                {
                    throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath_EmptySegment(targetPath));
                }

                if (targetSegment[0] == '@' && i != targetSegments.Length - 1)
                {
                    throw new ODataException(Strings.EpmTargetTree_AttributeInMiddle(targetSegment));
                }

                foundSegment = activeSubSegments.SingleOrDefault(
                    segment => segment.SegmentName == targetSegment &&
                    (epmInfo.IsSyndicationMapping || segment.SegmentNamespaceUri == namespaceUri));

                if (foundSegment != null)
                {
                    currentSegment = foundSegment;
                }
                else
                {
                    currentSegment = new EpmTargetPathSegment(targetSegment, namespaceUri, namespacePrefix, currentSegment);

                    if (targetSegment[0] == '@')
                    {
                        activeSubSegments.Insert(0, currentSegment);
                    }
                    else
                    {
                        activeSubSegments.Add(currentSegment);
                    }
                }

                activeSubSegments = currentSegment.SubSegments;
            }

            if (currentSegment.EpmInfo != null)
            {
                // Two EpmAttributes with same TargetName in the inheritance hierarchy
                throw new ODataException(Strings.EpmTargetTree_DuplicateEpmAttributesWithSameTargetName(currentSegment.EpmInfo.DefiningType.ODataFullName(), EpmTargetTree.GetPropertyNameFromEpmInfo(currentSegment.EpmInfo), currentSegment.EpmInfo.Attribute.SourcePath, epmInfo.Attribute.SourcePath));
            }

            // Increment the number of properties for which KeepInContent is false
            if (!epmInfo.Attribute.KeepInContent)
            {
                this.countOfNonContentV2Mappings++;
            }

            currentSegment.EpmInfo = epmInfo;

            // Mixed content is dis-allowed.
            List <EntityPropertyMappingAttribute> conflictingAttributes = new List <EntityPropertyMappingAttribute>(2);

            if (EpmTargetTree.HasMixedContent(this.NonSyndicationRoot, conflictingAttributes))
            {
                Debug.Assert(conflictingAttributes.Count == 2, "Expected to find exactly two conflicting attributes.");
                throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath_MixedContent(conflictingAttributes[0].TargetPath, conflictingAttributes[1].TargetPath));
            }
        }
Example #19
0
        /// <summary>
        /// Writes an EPM attribute target.
        /// </summary>
        /// <param name="writer">The writer to write to.</param>
        /// <param name="targetSegment">The target segment describing the attribute to write.</param>
        /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param>
        /// <param name="entityType">The type of the entry.</param>
        /// <param name="alreadyDeclaredPrefix">The name of the prefix if it was already declared.</param>
        private void WriteAttributeEpm(
            XmlWriter writer, 
            EpmTargetPathSegment targetSegment, 
            EntryPropertiesValueCache epmValueCache, 
            IEdmEntityTypeReference entityType, 
            ref string alreadyDeclaredPrefix)
        {
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(targetSegment != null && targetSegment.IsAttribute, "Only attribute target segments are supported by this method.");
            Debug.Assert(targetSegment.HasContent, "Attribute target segments must have content.");

            string textPropertyValue = this.GetEntryPropertyValueAsText(targetSegment, epmValueCache, entityType);
            Debug.Assert(textPropertyValue != null, "Text value of a property mapped to attribute must not be null, the GetEntryPropertyValueAsText should take care of that.");

            // If the prefix is null, the WCF DS will still write it as the default namespace, so we need it to be an empty string.
            string attributePrefix = targetSegment.SegmentNamespacePrefix ?? string.Empty;
            writer.WriteAttributeString(attributePrefix, targetSegment.AttributeName, targetSegment.SegmentNamespaceUri, textPropertyValue);

            // Write out the declaration explicitely only if the prefix is not empty (just like the WCF DS does)
            if (attributePrefix.Length > 0)
            {
                WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix);
            }
        }
Example #20
0
        /// <summary>
        /// Given a target segment the method returns the text value of the property mapped to that segment to be used in EPM.
        /// </summary>
        /// <param name="targetSegment">The target segment to read the value for.</param>
        /// <param name="epmValueCache">The entry EPM value cache to use.</param>
        /// <param name="entityType">The entity type of the entry being processed.</param>
        /// <returns>The test representation of the value, or the method throws if the text representation was not possible to obtain.</returns>
        private string GetEntryPropertyValueAsText(
            EpmTargetPathSegment targetSegment, 
            EntryPropertiesValueCache epmValueCache, 
            IEdmEntityTypeReference entityType)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(targetSegment.HasContent, "The target segment to read property for must have content.");
            Debug.Assert(targetSegment.EpmInfo != null, "The EPM info must be available on the target segment to read its property.");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");
            Debug.Assert(entityType != null, "entityType != null");

            object propertyValue = this.ReadEntryPropertyValue(
                targetSegment.EpmInfo,
                epmValueCache,
                entityType);
            if (propertyValue == null)
            {
                // nulls are written out as empty strings always (and they're written into content as well)
                return string.Empty;
            }

            return EpmWriterUtils.GetPropertyValueAsText(propertyValue);
        }
        /// <summary>
        /// Reads an attribute for custom EPM.
        /// </summary>
        /// <param name="entryState">The reader entry state for the entry being read.</param>
        /// <param name="epmTargetPathSegmentForElement">The EPM target segment for the element to which the attribute belongs.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Attribute - the attribute to read.
        /// Post-Condition: XmlNodeType.Attribute - the same attribute, the method doesn't move the reader.
        /// 
        /// The method works on any attribute, it checks if the attribute should be used for EPM or not.
        /// </remarks>
        private void ReadCustomEpmAttribute(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegmentForElement)
        {
            Debug.Assert(entryState != null, "entryState != null");
            this.AssertXmlCondition(XmlNodeType.Attribute);
            Debug.Assert(epmTargetPathSegmentForElement != null, "epmTargetPathSegmentForElement != null");

            string localName = this.XmlReader.LocalName;
            string namespaceUri = this.XmlReader.NamespaceURI;
            EpmTargetPathSegment attributeSegment = epmTargetPathSegmentForElement.SubSegments.FirstOrDefault(
                segment => segment.IsAttribute && 
                string.CompareOrdinal(segment.AttributeName, localName) == 0 &&
                string.CompareOrdinal(segment.SegmentNamespaceUri, namespaceUri) == 0);
            if (attributeSegment != null)
            {
                // Don't add values which we already have
                // Both WCF DS client and server will only read the first value from custom EPM for any given EPM info.
                // It also follows the behavior for syndication EPM, where we read only the first author if there are multiple (for example).
                if (!entryState.EpmCustomReaderValueCache.Contains(attributeSegment.EpmInfo))
                {
                    // Note that there's no way for an attribute to specify null value.
                    entryState.EpmCustomReaderValueCache.Add(attributeSegment.EpmInfo, this.XmlReader.Value);
                }
            }
        }
 private void ReadContributorElement(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegment)
 {
     if (this.ShouldReadCollectionElement(entryState.AtomEntryMetadata.Contributors.Any<AtomPersonMetadata>()))
     {
         AtomMetadataReaderUtils.AddContributorToEntryMetadata(entryState.AtomEntryMetadata, base.ReadAtomPersonConstruct(epmTargetPathSegment));
     }
     else
     {
         base.XmlReader.Skip();
     }
 }
Example #23
0
        /// <summary>
        /// Writes an EPM element target.
        /// </summary>
        /// <param name="writer">The writer to write to.</param>
        /// <param name="targetSegment">The target segment describing the element to write.</param>
        /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param>
        /// <param name="entityType">The type of the entry.</param>
        /// <param name="alreadyDeclaredPrefix">The name of the prefix if it was already declared.</param>
        private void WriteElementEpm(
            XmlWriter writer, 
            EpmTargetPathSegment targetSegment, 
            EntryPropertiesValueCache epmValueCache, 
            IEdmEntityTypeReference entityType, 
            ref string alreadyDeclaredPrefix)
        {
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(targetSegment != null && !targetSegment.IsAttribute, "Only element target segments are supported by this method.");

            // If the prefix is null, the WCF DS will still write it as the default namespace, so we need it to be an empty string.
            string elementPrefix = targetSegment.SegmentNamespacePrefix ?? string.Empty;
            writer.WriteStartElement(elementPrefix, targetSegment.SegmentName, targetSegment.SegmentNamespaceUri);

            // Write out the declaration explicitly only if the prefix is not empty (just like the WCF DS does)
            if (elementPrefix.Length > 0)
            {
                WriteNamespaceDeclaration(writer, targetSegment, ref alreadyDeclaredPrefix);
            }

            // Serialize the sub segment attributes first
            foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments)
            {
                if (subSegment.IsAttribute)
                {
                    this.WriteAttributeEpm(writer, subSegment, epmValueCache, entityType, ref alreadyDeclaredPrefix);
                }
            }

            if (targetSegment.HasContent)
            {
                Debug.Assert(!targetSegment.SubSegments.Any(subSegment => !subSegment.IsAttribute), "If the segment has a content, it must not have any element children.");

                string textPropertyValue = this.GetEntryPropertyValueAsText(targetSegment, epmValueCache, entityType);
                ODataAtomWriterUtils.WriteString(writer, textPropertyValue);
            }
            else
            {
                // Serialize the sub segment elements now
                foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments)
                {
                    if (!subSegment.IsAttribute)
                    {
                        this.WriteElementEpm(writer, subSegment, epmValueCache, entityType, ref alreadyDeclaredPrefix);
                    }
                }
            }

            // Close the element
            writer.WriteEndElement();
        }
        /// <summary>
        /// Reads an element for custom EPM.
        /// </summary>
        /// <param name="entryState">The reader entry state for the entry being read.</param>
        /// <param name="epmTargetPathSegment">The EPM target segment for the parent element to which the element belongs.</param>
        /// <returns>true if a mapping for the current custom element was found and the element was read; otherwise false.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - the element to read.
        /// Post-Condition: Any                 - the node after the element which was read.
        /// 
        /// The method works on any element, it checks if the element should be used for EPM or not.
        /// </remarks>
        private bool TryReadCustomEpmElement(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegment)
        {
            Debug.Assert(entryState != null, "entryState != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(epmTargetPathSegment != null, "epmTargetPathSegment != null");

            string localName = this.XmlReader.LocalName;
            string namespaceUri = this.XmlReader.NamespaceURI;
            EpmTargetPathSegment elementSegment = epmTargetPathSegment.SubSegments.FirstOrDefault(
                segment => !segment.IsAttribute &&
                string.CompareOrdinal(segment.SegmentName, localName) == 0 &&
                string.CompareOrdinal(segment.SegmentNamespaceUri, namespaceUri) == 0);

            if (elementSegment == null)
            {
                // Skip elements that are not part of EPM
                return false;
            }

            if (elementSegment.HasContent && entryState.EpmCustomReaderValueCache.Contains(elementSegment.EpmInfo))
            {
                // Skip elements for which we already have value.
                // Both WCF DS client and server will only read the first value from custom EPM for any given EPM info.
                // It also follows the behavior for syndication EPM, where we read only the first author if there are multiple (for example).
                // We don't want to try to parse such elements since the parsing itself may fail and we should not be failing on values
                // which we don't care about.
                return false;
            }

            while (this.XmlReader.MoveToNextAttribute())
            {
                this.ReadCustomEpmAttribute(entryState, elementSegment);
            }

            this.XmlReader.MoveToElement();

            if (elementSegment.HasContent)
            {
                string stringValue;

                // Read the value of the element.
                stringValue = this.ReadElementStringValue();

                entryState.EpmCustomReaderValueCache.Add(elementSegment.EpmInfo, stringValue);
            }
            else
            {
                if (!this.XmlReader.IsEmptyElement)
                {
                    // Move to the first child node of the element.
                    this.XmlReader.Read();

                    while (this.XmlReader.NodeType != XmlNodeType.EndElement)
                    {
                        switch (this.XmlReader.NodeType)
                        {
                            case XmlNodeType.EndElement:
                                break;

                            case XmlNodeType.Element:
                                if (!this.TryReadCustomEpmElement(entryState, elementSegment))
                                {
                                    this.XmlReader.Skip();
                                }

                                break;

                            default:
                                this.XmlReader.Skip();
                                break;
                        }
                    }
                }

                // Read the end element or the empty start element.
                this.XmlReader.Read();
            }

            return true;
        }
Example #25
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            string targetPath            = epmInfo.Attribute.TargetPath;
            string namespaceUri          = epmInfo.Attribute.TargetNamespaceUri;
            string targetNamespacePrefix = epmInfo.Attribute.TargetNamespacePrefix;
            EpmTargetPathSegment         parentSegment = epmInfo.IsSyndicationMapping ? this.SyndicationRoot : this.NonSyndicationRoot;
            IList <EpmTargetPathSegment> subSegments   = parentSegment.SubSegments;

            string[]             strArray = targetPath.Split(new char[] { '/' });
            EpmTargetPathSegment segment2 = null;

            for (int i = 0; i < strArray.Length; i++)
            {
                string targetSegment = strArray[i];
                if (targetSegment.Length == 0)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_InvalidTargetPath_EmptySegment(targetPath));
                }
                if ((targetSegment[0] == '@') && (i != (strArray.Length - 1)))
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_AttributeInMiddle(targetSegment));
                }
                segment2 = subSegments.SingleOrDefault <EpmTargetPathSegment>(delegate(EpmTargetPathSegment segment) {
                    if (!(segment.SegmentName == targetSegment))
                    {
                        return(false);
                    }
                    if (!epmInfo.IsSyndicationMapping)
                    {
                        return(segment.SegmentNamespaceUri == namespaceUri);
                    }
                    return(true);
                });
                if (segment2 != null)
                {
                    parentSegment = segment2;
                }
                else
                {
                    parentSegment = new EpmTargetPathSegment(targetSegment, namespaceUri, targetNamespacePrefix, parentSegment);
                    if (targetSegment[0] == '@')
                    {
                        subSegments.Insert(0, parentSegment);
                    }
                    else
                    {
                        subSegments.Add(parentSegment);
                    }
                }
                subSegments = parentSegment.SubSegments;
            }
            if (parentSegment.EpmInfo != null)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_DuplicateEpmAttributesWithSameTargetName(parentSegment.EpmInfo.DefiningType.ODataFullName(), GetPropertyNameFromEpmInfo(parentSegment.EpmInfo), parentSegment.EpmInfo.Attribute.SourcePath, epmInfo.Attribute.SourcePath));
            }
            if (!epmInfo.Attribute.KeepInContent)
            {
                this.countOfNonContentV2Mappings++;
            }
            parentSegment.EpmInfo = epmInfo;
            List <EntityPropertyMappingAttribute> ancestorsWithContent = new List <EntityPropertyMappingAttribute>(2);

            if (HasMixedContent(this.NonSyndicationRoot, ancestorsWithContent))
            {
                throw new ODataException(Microsoft.Data.OData.Strings.EpmTargetTree_InvalidTargetPath_MixedContent(ancestorsWithContent[0].TargetPath, ancestorsWithContent[1].TargetPath));
            }
        }
        /// <summary>
        /// Reads a contributor element.
        /// </summary>
        /// <param name="entryState">The reader entry state for the entry being read.</param>
        /// <param name="epmTargetPathSegment">The EPM target path segment for the element to read, or null if no EPM for that element is defined.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element (atom:contributor) - the atom:contributor element to read.
        /// Post-Condition: Any                                    - the node after the atom:contributor element which was read.
        /// </remarks>
        private void ReadContributorElement(IODataAtomReaderEntryState entryState, EpmTargetPathSegment epmTargetPathSegment)
        {
            Debug.Assert(entryState != null, "entryState != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomContributorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace,
                "Only atom:contributor elements can be read by this method.");

            if (this.ShouldReadCollectionElement(entryState.AtomEntryMetadata.Contributors.Any()))
            {
                AtomMetadataReaderUtils.AddContributorToEntryMetadata(
                    entryState.AtomEntryMetadata,
                    this.ReadAtomPersonConstruct(epmTargetPathSegment));
            }
            else
            {
                this.XmlReader.Skip();
            }
        }
        /// <summary>
        /// Used for creating non-root nodes in the syndication/custom trees.
        /// </summary>
        /// <param name="segmentName">Name of xml element/attribute</param>
        /// <param name="segmentNamespaceUri">URI of the namespace for <paramref name="segmentName"/></param>
        /// <param name="segmentNamespacePrefix">Namespace prefix to be used for <paramref name="segmentNamespaceUri"/></param>
        /// <param name="parentSegment">Reference to the parent node if this is a sub-node, useful for traversals in visitors</param>
        internal EpmTargetPathSegment(string segmentName, string segmentNamespaceUri, string segmentNamespacePrefix, EpmTargetPathSegment parentSegment)
            : this()
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(segmentName == null || segmentName.Length > 0, "Empty segment name is not allowed.");

            this.segmentName = segmentName;
            this.segmentNamespaceUri = segmentNamespaceUri;
            this.segmentNamespacePrefix = segmentNamespacePrefix;
            this.parentSegment = parentSegment;

            if (!string.IsNullOrEmpty(segmentName) && segmentName[0] == '@')
            {
                this.segmentAttributeName = segmentName.Substring(1);
            }
        }
 protected bool ShouldReadElement(EpmTargetPathSegment parentSegment, string segmentName, out EpmTargetPathSegment subSegment)
 {
     Func<EpmTargetPathSegment, bool> predicate = null;
     subSegment = null;
     if (parentSegment != null)
     {
         if (predicate == null)
         {
             predicate = segment => string.CompareOrdinal(segment.SegmentName, segmentName) == 0;
         }
         subSegment = parentSegment.SubSegments.FirstOrDefault<EpmTargetPathSegment>(predicate);
         if (((subSegment != null) && (subSegment.EpmInfo != null)) && subSegment.EpmInfo.Attribute.KeepInContent)
         {
             return this.ReadAtomMetadata;
         }
     }
     if (subSegment == null)
     {
         return this.ReadAtomMetadata;
     }
     return true;
 }