Пример #1
0
        /// <summary>
        /// Determines if the cache already contains a value for the specified EPM mapping.
        /// </summary>
        /// <param name="epmInfo">The EPM info for the EPM mapping to look for.</param>
        /// <returns>true if the cache already contains a value for this mapping, false otherwise.</returns>
        internal bool Contains(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(epmInfo != null, "epmInfo != null");

            return(this.customEpmValues.Any(epmValue => object.ReferenceEquals(epmValue.Key, epmInfo)));
        }
Пример #2
0
        /// <summary>
        /// Reads a leaf segment which maps to a property value.
        /// </summary>
        /// <param name="targetSegment">The segment being read.</param>
        /// <param name="entryMetadata">The ATOM entry metadata to read from.</param>
        private void ReadPropertyValueSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata)
        {
            Debug.Assert(targetSegment != null, "targetSegment != null");
            Debug.Assert(entryMetadata != null, "entryMetadata != null");

            EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;

            Debug.Assert(
                epmInfo != null && epmInfo.Attribute != null,
                "If the segment has content it must have EpmInfo which in turn must have the Epm attribute");

            switch (epmInfo.Attribute.TargetSyndicationItem)
            {
            case SyndicationItemProperty.Updated:
                if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                {
                    if (entryMetadata.UpdatedString != null)
                    {
                        this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.UpdatedString);
                    }
                }
                else if (entryMetadata.Updated.HasValue)
                {
                    this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Updated.Value));
                }

                break;

            case SyndicationItemProperty.Published:
                if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                {
                    if (entryMetadata.PublishedString != null)
                    {
                        this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.PublishedString);
                    }
                }
                else if (entryMetadata.Published.HasValue)
                {
                    this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Published.Value));
                }

                break;

            case SyndicationItemProperty.Rights:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Rights);
                break;

            case SyndicationItemProperty.Summary:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Summary);
                break;

            case SyndicationItemProperty.Title:
                this.ReadTextConstructEpm(targetSegment, entryMetadata.Title);
                break;

            default:
                throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadEntryEpm_ContentTarget));
            }
        }
Пример #3
0
 /// <summary>
 /// Sets the value read from EPM to a property on an entry.
 /// </summary>
 /// <param name="epmInfo">The EPM info for the mapping for which the value was read.</param>
 /// <param name="propertyValue">The property value read, if the value was specified as null then this should be null,
 /// if the value was missing the method should not be called at all.
 /// For primitive properties this should be the string value, for all other properties this should be the exact value type.</param>
 protected void SetEntryEpmValue(EntityPropertyMappingInfo epmInfo, object propertyValue)
 {
     this.SetEpmValue(
         this.entryState.Entry.Properties.ToReadOnlyEnumerable("Properties"),
         this.entryState.EntityType.ToTypeReference(),
         epmInfo,
         propertyValue);
 }
Пример #4
0
        /// <summary>
        /// Adds a value to cache.
        /// </summary>
        /// <param name="epmInfo">The EPM info for the mapping according to which the value was read.</param>
        /// <param name="value">The value to cache.</param>
        /// <remarks>
        /// The method will only store the first value for any given EPM info, since in custom EPM
        /// only the first occurrence of the element/attribute is used, the others are ignored.
        /// </remarks>
        internal void Add(EntityPropertyMappingInfo epmInfo, string value)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(!this.Contains(epmInfo), "We should not be trying to add more than one value for any given mapping.");

            this.customEpmValues.Add(new KeyValuePair <EntityPropertyMappingInfo, string>(epmInfo, value));
        }
Пример #5
0
 private static string GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo)
 {
     if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty)
     {
         return epmInfo.Attribute.TargetPath;
     }
     return epmInfo.Attribute.TargetSyndicationItem.ToString();
 }
Пример #6
0
 /// <summary>
 /// Sets the value read from EPM to a property on an entry.
 /// </summary>
 /// <param name="epmInfo">The EPM info for the mapping for which the value was read.</param>
 /// <param name="propertyValue">The property value read, if the value was specified as null then this should be null,
 /// if the value was missing the method should not be called at all.
 /// For primitive properties this should be the string value, for all other properties this should be the exact value type.</param>
 protected void SetEntryEpmValue(EntityPropertyMappingInfo epmInfo, object propertyValue)
 {
     this.SetEpmValue(
         ReaderUtils.GetPropertiesList(this.entryState.Entry.Properties),
         this.entryState.EntityType.ToTypeReference(),
         epmInfo,
         propertyValue);
 }
Пример #7
0
 private static string GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo)
 {
     if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty)
     {
         return epmInfo.Attribute.TargetPath;
     }
     if (!epmInfo.IsEFProvider)
     {
         return epmInfo.Attribute.TargetSyndicationItem.ToString();
     }
     return EpmTranslate.MapSyndicationPropertyToEpmTargetPath(epmInfo.Attribute.TargetSyndicationItem);
 }
Пример #8
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)
        {
            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);
            }
        }
Пример #9
0
        internal static EntityPropertyMappingAttribute GetEntityPropertyMapping(EpmSourcePathSegment epmSourcePathSegment)
        {
            if (epmSourcePathSegment == null)
            {
                return(null);
            }
            EntityPropertyMappingInfo epmInfo = epmSourcePathSegment.EpmInfo;

            if (epmInfo == null)
            {
                return(null);
            }
            return(epmInfo.Attribute);
        }
Пример #10
0
        /// <summary>
        /// Given an <see cref="EntityPropertyMappingInfo"/> gives the correct target path for it
        /// </summary>
        /// <param name="epmInfo">Given <see cref="EntityPropertyMappingInfo"/></param>
        /// <returns>String with the correct value for the target path</returns>
        private static String GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo)
        {
            if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty)
            {
                return(epmInfo.Attribute.TargetPath);
            }

            // for EF provider we want to return a name that corresponds to attribute in the edmx file while for CLR provider
            // and the client we want to return a name that corresponds to the enum value used in EntityPropertyMapping attribute.
            return
                (#if ASTORIA_SERVER
                 epmInfo.IsEFProvider ? EpmTranslate.MapSyndicationPropertyToEpmTargetPath(epmInfo.Attribute.TargetSyndicationItem) :
#endif
                 epmInfo.Attribute.TargetSyndicationItem.ToString());
        }
Пример #11
0
        /// <summary>
        /// Reads a property value starting on an entry.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="epmValueCache">The EPM value cache for the entry to read from.</param>
        /// <param name="entityType">The type of the entry.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        protected object ReadEntryPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            EntryPropertiesValueCache epmValueCache,
            IEdmEntityTypeReference entityType)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > 0, "The PropertyValuePath must not be empty for an entry property.");
            Debug.Assert(entityType != null, "entityType != null");

            // TODO - It might be possible to avoid the "value" type checks below if we do property value validation based on the type
            return(this.ReadPropertyValue(
                       epmInfo,
                       epmValueCache.EntryProperties,
                       0,
                       entityType,
                       epmValueCache));
        }
Пример #12
0
        /// <summary>
        /// Sets the value read from EPM to a property on an entry.
        /// </summary>
        /// <param name="targetList">The target list, which is a list of properties (on entry or complex value).</param>
        /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity or complex).</param>
        /// <param name="epmInfo">The EPM info for the mapping for which the value was read.</param>
        /// <param name="propertyValue">The property value read, if the value was specified as null then this should be null,
        /// if the value was missing the method should not be called at all.
        /// For primitive properties this should be the string value, for all other properties this should be the exact value type.</param>
        protected void SetEpmValue(
            IList targetList,
            IEdmTypeReference targetTypeReference,
            EntityPropertyMappingInfo epmInfo,
            object propertyValue)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(targetTypeReference != null, "targetTypeReference != null");
            Debug.Assert(targetList != null, "targetList != null");
            Debug.Assert(
                targetTypeReference.IsODataEntityTypeKind() || targetTypeReference.IsODataComplexTypeKind(),
                "Only entity and complex types can have an EPM value set on them.");

            this.SetEpmValueForSegment(
                epmInfo,
                0,
                targetTypeReference.AsStructuredOrNull(),
                (List <ODataProperty>)targetList,
                propertyValue);
        }
Пример #13
0
        /// <summary>
        /// Gets the <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="epmSourcePathSegment"/>.
        /// </summary>
        /// <param name="epmSourcePathSegment">The EPM source path segment to get the <see cref="EntityPropertyMappingAttribute"/> from.</param>
        /// <returns>The <see cref="EntityPropertyMappingAttribute"/> for the specified <paramref name="epmSourcePathSegment"/> or null if none exists.</returns>
        internal static EntityPropertyMappingAttribute GetEntityPropertyMapping(EpmSourcePathSegment epmSourcePathSegment)
        {
            DebugUtils.CheckNoExternalCallers();

            if (epmSourcePathSegment == null)
            {
                return(null);
            }

            EntityPropertyMappingInfo epmInfo = epmSourcePathSegment.EpmInfo;

            if (epmInfo == null)
            {
                return(null);
            }

            EntityPropertyMappingAttribute epmAttribute = epmInfo.Attribute;

            Debug.Assert(epmAttribute != null, "Attribute should always be initialized for EpmInfo.");
            return(epmAttribute);
        }
Пример #14
0
        /// <summary>
        /// Reads a property value starting on a complex value.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="complexValue">The complex value to start with.</param>
        /// <param name="epmValueCache">The EPM value cache to use.</param>
        /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
        /// <param name="complexType">The type of the complex value.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        private object ReadComplexPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            ODataComplexValue complexValue,
            EpmValueCache epmValueCache,
            int sourceSegmentIndex,
            IEdmComplexTypeReference complexType)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index.");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");
            Debug.Assert(sourceSegmentIndex >= 0, "sourceSegmentIndex >= 0");
            Debug.Assert(complexType != null, "complexType != null");
            Debug.Assert(complexValue != null, "complexValue != null");

            return(this.ReadPropertyValue(
                       epmInfo,
                       EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false),
                       sourceSegmentIndex,
                       complexType,
                       epmValueCache));
        }
		/// <summary>Compares the defining type of this info and other EpmInfo object.</summary>
		/// <param name="other">The other EpmInfo object to compare to.</param>
		/// <returns>true if the defining types are the same</returns>
		internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other)
		{
			DebugUtils.CheckNoExternalCallers();
			Debug.Assert(other != null, "other != null");

			return this.DefiningType == other.DefiningType;
		}
		/// <summary>
		/// Trims the start of the property value path by removing the path to the multivalue property.
		/// </summary>
		/// <param name="multiValueEpmInfo">Epm info for the multivalue property itself..</param>
		internal void TrimMultiValueItemPropertyPath(EntityPropertyMappingInfo multiValueEpmInfo)
		{
			DebugUtils.CheckNoExternalCallers();
			Debug.Assert(multiValueEpmInfo != null, "multiValueEpmInfo != null");
			Debug.Assert(multiValueEpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty, "The multiValueEpmInfo must be a multiValue property info.");
#if DEBUG
			Debug.Assert(multiValueEpmInfo.propertyValuePath.Length <= this.propertyValuePath.Length, "The prefix is longer than the actual source path.");
			for (int i = 0; i < multiValueEpmInfo.propertyValuePath.Length; i++)
			{
				Debug.Assert(multiValueEpmInfo.propertyValuePath[i] == this.propertyValuePath[i], "The prefix doesn't match the actual source path.");
			}
#endif

			this.propertyValuePath = this.propertyValuePath.Skip(multiValueEpmInfo.propertyValuePath.Length).ToArray();
		}
Пример #17
0
 internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other)
 {
     return (this.DefiningType == other.DefiningType);
 }
Пример #18
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));
     }
 }
Пример #19
0
 internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other)
 {
     return ((IEdmType) this.DefiningType).IsEquivalentTo(((IEdmType) other.DefiningType));
 }
Пример #20
0
        private void SetEpmValueForSegment(
            EntityPropertyMappingInfo epmInfo,
            int propertyValuePathIndex,
            IEdmStructuredTypeReference segmentStructuralTypeReference,
            List <ODataProperty> existingProperties,
            object propertyValue)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(propertyValuePathIndex < epmInfo.PropertyValuePath.Length, "The propertyValuePathIndex is out of bounds.");
            Debug.Assert(existingProperties != null, "existingProperties != null");

            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            // Do not set out-of-content values if the EPM is defined as KeepInContent=true.
            if (epmInfo.Attribute.KeepInContent)
            {
                return;
            }

            // Try to find the property in the existing properties
            // If the property value is atomic from point of view of EPM (non-streaming collection or primitive) then if it already exists
            // it must have been in-content, and thus we leave it as is (note that two EPMs can't map to the same property, we verify that upfront).
            // If the property value is non-atomic, then it is a complex value, we might want to merge the new value comming from EPM with it.
            ODataProperty     existingProperty     = existingProperties.FirstOrDefault(p => string.CompareOrdinal(p.Name, propertyName) == 0);
            ODataComplexValue existingComplexValue = null;

            if (existingProperty != null)
            {
                // In case the property exists and it's a complex value we will try to merge.
                // Note that if the property is supposed to be complex, but it already has a null value, then the null wins.
                // Since in-content null complex value wins over any EPM complex value.
                existingComplexValue = existingProperty.Value as ODataComplexValue;
                if (existingComplexValue == null)
                {
                    return;
                }
            }

            IEdmProperty propertyMetadata = segmentStructuralTypeReference.FindProperty(propertyName);

            Debug.Assert(propertyMetadata != null || segmentStructuralTypeReference.IsOpen(), "We should have verified that if the property is not declared the type must be open.");

            if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1)
            {
                throw new ODataException(o.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
            }

            // Open properties in EPM are by default of type Edm.String - there's no way to specify a typename in EPM
            // consumer is free to do the conversion later on if it needs to.
            // Note that this effectively means that ODataMessageReaderSettings.DisablePrimitiveTypeConversion is as if it's turned on for open EPM properties.
            IEdmTypeReference propertyType;

            if (propertyMetadata == null ||
                (this.MessageReaderSettings.DisablePrimitiveTypeConversion && propertyMetadata.Type.IsODataPrimitiveTypeKind()))
            {
                propertyType = EdmCoreModel.Instance.GetString(/*nullable*/ true);
            }
            else
            {
                propertyType = propertyMetadata.Type;
            }

            // NOTE: WCF DS Server only applies the values when
            // - It's an open property
            // - It's not a key property
            // - It's a key property and it's a POST operation
            // ODataLib here will always set the property though.
            switch (propertyType.TypeKind())
            {
            case EdmTypeKind.Primitive:
            {
                if (propertyType.IsStream())
                {
                    throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                }

                object primitiveValue;
                if (propertyValue == null)
                {
                    ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, propertyType, this.atomInputContext.MessageReaderSettings, /*validateNullValue*/ true, this.atomInputContext.Version);
                    primitiveValue = null;
                }
                else
                {
                    // Convert the value to the desired target type
                    primitiveValue = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, propertyType.AsPrimitive());
                }

                this.AddEpmPropertyValue(existingProperties, propertyName, primitiveValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
            }

            break;

            case EdmTypeKind.Complex:
                // Note: Unlike WCF DS we don't have a preexisting instance to override (since complex values are atomic, so we should not updated them)
                // In our case the complex value either doesn't exist yet on the entry being reported (easy, create it)
                // or it exists, but then it was created during reading of previous normal or EPM properties for this entry. It never exists before
                // we ever get to see the entity. So in our case we will never recreate the complex value, we always start with new one
                // and update it with new properties as they come. (Next time we will start over with a new complex value.)
                Debug.Assert(
                    existingComplexValue == null || (existingProperty != null && existingProperty.Value == existingComplexValue),
                    "If we have existing complex value, we must have an existing property as well.");
                Debug.Assert(
                    epmInfo.PropertyValuePath.Length > propertyValuePathIndex + 1,
                    "Complex value can not be a leaf segment in the source property path. We should have failed constructing the EPM trees for it.");

                if (existingComplexValue == null)
                {
                    Debug.Assert(existingProperty == null, "If we don't have an existing complex value, then we must not have an existing property at all.");

                    // Create a new complex value and set its type name to the type name of the property type (in case of EPM we never have type name from the payload)
                    existingComplexValue = new ODataComplexValue
                    {
                        TypeName   = propertyType.ODataFullName(),
                        Properties = new ReadOnlyEnumerable <ODataProperty>()
                    };

                    this.AddEpmPropertyValue(existingProperties, propertyName, existingComplexValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
                }

                // Get the properties list of the complex value and recursively set the next EPM segment value to it.
                // Note that on inner complex value we don't need to check for duplicate properties
                // because EPM will never add a property which already exists (see the start of this method).
                IEdmComplexTypeReference complexPropertyTypeReference = propertyType.AsComplex();
                Debug.Assert(complexPropertyTypeReference != null, "complexPropertyTypeReference != null");
                this.SetEpmValueForSegment(
                    epmInfo,
                    propertyValuePathIndex + 1,
                    complexPropertyTypeReference,
                    ReaderUtils.GetPropertiesList(existingComplexValue.Properties),
                    propertyValue);

                break;

            case EdmTypeKind.Collection:
                Debug.Assert(propertyType.IsNonEntityODataCollectionTypeKind(), "Collection types in EPM must be atomic.");

                // In this case the property value is the internal list of items.
                // Create a new collection value and set the list as the list of items on it.
                ODataCollectionValue collectionValue = new ODataCollectionValue
                {
                    TypeName = propertyType.ODataFullName(),
                    Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                };

                this.AddEpmPropertyValue(existingProperties, propertyName, collectionValue, segmentStructuralTypeReference.IsODataEntityTypeKind());

                break;

            default:
                throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
        protected override void Serialize(EpmTargetPathSegment targetSegment, EpmSerializationKind kind)
        {
            if (targetSegment.HasContent)
            {
                EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;
                Object propertyValue;

                try
                {
                    propertyValue = epmInfo.PropValReader.DynamicInvoke(this.Element);
                }
                catch (System.Reflection.TargetInvocationException)
                {
                    throw;
                }

                String          contentType;
                Action <String> contentWriter;

                switch (epmInfo.Attribute.TargetTextContentKind)
                {
                case SyndicationTextContentKind.Html:
                    contentType   = "html";
                    contentWriter = this.Target.WriteString;
                    break;

                case SyndicationTextContentKind.Xhtml:
                    contentType   = "xhtml";
                    contentWriter = this.Target.WriteRaw;
                    break;

                default:
                    contentType   = "text";
                    contentWriter = this.Target.WriteString;
                    break;
                }

                Action <String, bool, bool> textSyndicationWriter = (c, nonTextPossible, atomDateConstruct) =>
                {
                    this.Target.WriteStartElement(c, XmlConstants.AtomNamespace);
                    if (nonTextPossible)
                    {
                        this.Target.WriteAttributeString(XmlConstants.AtomTypeAttributeName, String.Empty, contentType);
                    }

                    String textPropertyValue =
                        propertyValue != null?ClientConvert.ToString(propertyValue, atomDateConstruct) :
                            atomDateConstruct?ClientConvert.ToString(DateTime.MinValue, atomDateConstruct) :
                                String.Empty;

                    contentWriter(textPropertyValue);
                    this.Target.WriteEndElement();
                };

                switch (epmInfo.Attribute.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.ContributorEmail:
                    textSyndicationWriter(XmlConstants.AtomEmailElementName, false, false);
                    break;

                case SyndicationItemProperty.AuthorName:
                case SyndicationItemProperty.ContributorName:
                    textSyndicationWriter(XmlConstants.AtomNameElementName, false, false);
                    this.authorNamePresent = true;
                    break;

                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorUri:
                    textSyndicationWriter(XmlConstants.AtomUriElementName, false, false);
                    break;

                case SyndicationItemProperty.Updated:
                    textSyndicationWriter(XmlConstants.AtomUpdatedElementName, false, true);
                    this.updatedPresent = true;
                    break;

                case SyndicationItemProperty.Published:
                    textSyndicationWriter(XmlConstants.AtomPublishedElementName, false, true);
                    break;

                case SyndicationItemProperty.Rights:
                    textSyndicationWriter(XmlConstants.AtomRightsElementName, true, false);
                    break;

                case SyndicationItemProperty.Summary:
                    textSyndicationWriter(XmlConstants.AtomSummaryElementName, true, false);
                    break;

                case SyndicationItemProperty.Title:
                    textSyndicationWriter(XmlConstants.AtomTitleElementName, true, false);
                    break;

                default:
                    Debug.Assert(false, "Unhandled SyndicationItemProperty enum value - should never get here.");
                    break;
                }
            }
            else
            {
                if (targetSegment.SegmentName == XmlConstants.AtomAuthorElementName)
                {
                    this.CreateAuthor(false);
                    base.Serialize(targetSegment, kind);
                    this.FinishAuthor();
                }
                else if (targetSegment.SegmentName == XmlConstants.AtomContributorElementName)
                {
                    this.Target.WriteStartElement(XmlConstants.AtomContributorElementName, XmlConstants.AtomNamespace);
                    base.Serialize(targetSegment, kind);
                    this.Target.WriteEndElement();
                }
                else
                {
                    Debug.Assert(false, "Only authors and contributors have nested elements");
                }
            }
        }
Пример #22
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;
            EpmTargetPathSegment multiValueSegment = null;
            for (int i = 0; i < targetSegments.Length; i++)
            {
                String targetSegment = targetSegments[i];

                if (targetSegment.Length == 0)
                {
                    throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath(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) &&
                    segment.MatchCriteria(epmInfo.CriteriaValue, epmInfo.Criteria));

                if (foundSegment != null)
                {
                    currentSegment = foundSegment;
                    if (currentSegment.EpmInfo != null && currentSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty)
                    {
                        multiValueSegment = currentSegment;
                    }
                }
                else
                {
                    currentSegment = new EpmTargetPathSegment(targetSegment, namespaceUri, namespacePrefix, currentSegment);
                    currentSegment.Criteria = epmInfo.Criteria;
                    currentSegment.CriteriaValue = epmInfo.CriteriaValue;

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

                activeSubSegments = currentSegment.SubSegments;
            }

            // If we're adding a multiValue property to already existing segment which maps to a non-multiValue property (no EpmInfo or one pointing to a non-multiValue property)
            // OR if we're adding a non-multiValue property to a segment which has multiValue in its path
            //   we need to fail, since it's invalid to have multiValue property being mapped to the same top-level element as anything else.
            if ((epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty && 
                    foundSegment != null &&
                    (foundSegment.EpmInfo == null || foundSegment.EpmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty)) ||
                (epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.None &&
                    multiValueSegment != null))
            {
                EntityPropertyMappingInfo multiValuePropertyEpmInfo = epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty ?
                    epmInfo : multiValueSegment.EpmInfo;

                // Trying to map MultiValue property to the same target as something else which was already mapped there
                // It is ok to map to atom:category and atom:link elements with different sources only if the criteria values are different.
                if (epmInfo.CriteriaValue != null)
                {
                    throw new ODataException(Strings.EpmTargetTree_MultiValueAndNormalPropertyMappedToTheSameConditionalTopLevelElement(
                        multiValuePropertyEpmInfo.Attribute.SourcePath,
                        epmInfo.DefiningType.Name,
                        EpmTargetTree.GetPropertyNameFromEpmInfo(multiValuePropertyEpmInfo),
                        epmInfo.CriteriaValue));
                }
                else
                {
                    throw new ODataException(Strings.EpmTargetTree_MultiValueAndNormalPropertyMappedToTheSameTopLevelElement(
                        multiValuePropertyEpmInfo.Attribute.SourcePath,
                        epmInfo.DefiningType.Name,
                        EpmTargetTree.GetPropertyNameFromEpmInfo(multiValuePropertyEpmInfo)));
                }
            }

            Debug.Assert(
                epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.None || epmInfo.IsSyndicationMapping,
                "Custom EPM mapping is not supported for multiValue properties.");

            // We only allow multiValues to map to ATOM constructs which can be repeated.
            if (epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty)
            {
                Debug.Assert(
                    epmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty, 
                    "Trying to add custom mapped property to a syndication target tree.");

                // Right now all the SyndicationItemProperty targets which do not have SyndicationParent.Entry are valid targets for multiValues
                // and all the ones which have SyndicationParent.Entry (Title, Updated etc.) are not valid targets for multiValues.
                if (epmInfo.SyndicationParent == EpmSyndicationParent.Entry)
                {
                    throw new ODataException(Strings.EpmTargetTree_MultiValueMappedToNonRepeatableAtomElement(
                        epmInfo.Attribute.SourcePath,
                        epmInfo.DefiningType.Name,
                        EpmTargetTree.GetPropertyNameFromEpmInfo(epmInfo)));
                }
            }

            Debug.Assert(
                epmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty || targetSegments.Length == 1,
                "MultiValue property itself can only be mapped to the top-level element.");

            if (currentSegment.EpmInfo != null)
            {
                if (currentSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty)
                {
                    Debug.Assert(
                        epmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                        "MultiValue property values can't be mapped directly to the top-level element content (no such syndication mapping exists).");

                    // The info we're trying to add is a multiValue property, which would mean two multiValue properties trying to map to the same top-level element.
                    // This can happen if the base type defines mapping for a multiValue property and the derived type defines it again
                    //   in which case we will try to add the derived type mapping again.
                    // So we need to check that these properties are from the same source
                    // It is ok to map to atom:category and atom:link elements with different sources only if the criteria values are different.
                    if (epmInfo.Attribute.SourcePath != currentSegment.EpmInfo.Attribute.SourcePath)
                    {
                        if (epmInfo.CriteriaValue != null)
                        {
                            throw new ODataException(Strings.EpmTargetTree_TwoMultiValuePropertiesMappedToTheSameConditionalTopLevelElement(
                                currentSegment.EpmInfo.Attribute.SourcePath,
                                epmInfo.Attribute.SourcePath,
                                epmInfo.DefiningType.Name,
                                epmInfo.CriteriaValue));
                        }
                        else
                        {
                            throw new ODataException(Strings.EpmTargetTree_TwoMultiValuePropertiesMappedToTheSameTopLevelElement(
                                currentSegment.EpmInfo.Attribute.SourcePath,
                                epmInfo.Attribute.SourcePath,
                                epmInfo.DefiningType.Name));
                        }
                    }

                    Debug.Assert(
                        !foundSegment.EpmInfo.DefiningTypesAreEqual(epmInfo),
                        "Trying to add a multiValue property mapping for the same property on the same type twice. The souce tree should have prevented this from happening.");

                    // If the sources are the same (and the types are different), we can safely overwrite the epmInfo
                    // with the new one (which is for the derived type)
                    // The epm info is stored below.
                }
                else
                {
                    Debug.Assert(
                        epmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                        "Only non-multiValue propeties should get here, we cover the rest above.");

                    // Two EpmAttributes with same TargetName in the inheritance hierarchy
                    throw new ODataException(Strings.EpmTargetTree_DuplicateEpmAttrsWithSameTargetName(EpmTargetTree.GetPropertyNameFromEpmInfo(currentSegment.EpmInfo), currentSegment.EpmInfo.DefiningType.Name, currentSegment.EpmInfo.Attribute.SourcePath, epmInfo.Attribute.SourcePath));
                }
            }

            // Increment the number of properties for which KeepInContent is false
            if (!epmInfo.Attribute.KeepInContent)
            {
                if (epmInfo.IsAtomLinkMapping || epmInfo.IsAtomCategoryMapping)
                {
                    this.countOfNonContentV3mappings++;
                }
                else
                {
                    this.countOfNonContentV2mappings++;
                }
            }

            currentSegment.EpmInfo = epmInfo;
            
            // Mixed content is dis-allowed. Since root has no ancestor, pass in false for ancestorHasContent
            if (EpmTargetTree.HasMixedContent(this.NonSyndicationRoot, false))
            {
                throw new ODataException(Strings.EpmTargetTree_InvalidTargetPath(targetPath));
            }
        }
Пример #23
0
        /// <summary>
        /// Validates the specified segment which is a subsegment of a MultiValue property or the MultiValue property segment itself.
        /// </summary>
        /// <param name="multiValuePropertyInfo">Info about the MultiValue property being processed. Used for exception messages only.</param>
        /// <param name="multiValueSegment">The segment belonging to a MultiValue property to validate.</param>
        /// <param name="resourceType">The resource type of the property represented by this segment (item type for the MultiValue property itself).</param>
        private static void ValidateMultiValueSegment(
            EntityPropertyMappingInfo multiValuePropertyInfo, 
            EpmSourcePathSegment multiValueSegment, 
            ResourceType resourceType)
        {
            Debug.Assert(
                multiValuePropertyInfo != null && multiValuePropertyInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                "multiValuePropertyName must be non-null and must be a MultiValue info.");
            Debug.Assert(multiValueSegment != null, "multiValueSegment != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(
                multiValueSegment.EpmInfo == null ||
                multiValueSegment.EpmInfo == multiValuePropertyInfo ||
                multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                "The specified segment does not belong to a MultiValue property subtree.");

            if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
            {
                Debug.Assert(
                    multiValueSegment.EpmInfo == null || multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                    "EPM source segment representing a complex property of a MultiValue property must not have an EpmInfo for the property itself.");

                // Verify that all properties of the complex type are mapped (have respective source segments)
                foreach (ResourceProperty property in resourceType.Properties)
                {
                    string propertyName = property.Name;
                    string resourceTypeName = resourceType.Name;
                    EpmSourcePathSegment subSegment = multiValueSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                    if (subSegment == null)
                    {
                        throw new ODataException(Strings.EpmSourceTree_NotAllMultiValueItemPropertiesMapped(
                            multiValuePropertyInfo.Attribute.SourcePath,
                            multiValuePropertyInfo.DefiningType.Name,
                            propertyName,
                            resourceTypeName));
                    }
                    else
                    {
                        ResourceType propertyType = property.ResourceType;

                        // Recursive call to verify the sub segment
                        // Note that we don't need to check for enless loops and recursion depth because we are effectively walking the EPM source tree
                        //   which itself can't have loops in it, and can't be infinite either. So if the metadata for a MultiValue property has loops in it
                        //   we would eventually fail to find a matching segment in the source tree and throw.
                        ValidateMultiValueSegment(multiValuePropertyInfo, subSegment, propertyType);
                    }
                }
            }
            else
            {
                Debug.Assert(multiValueSegment.EpmInfo != null, "Primitive value must have EpmInfo.");

                if (multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty)
                {
                    // MultiValue of primitive types
                    Debug.Assert(multiValueSegment.SubProperties.Count == 1, "Exactly one subproperty should be on a node representing a MultiValue property of primitive types.");
                    Debug.Assert(
                        multiValueSegment.SubProperties[0].IsMultiValueItemValue && multiValueSegment.SubProperties[0].EpmInfo != null &&
                        multiValueSegment.SubProperties[0].EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                        "The only subproperty of a collectin of primitive types should be the special item's value node.");
                    EpmSourcePathSegment leafSegment = multiValueSegment.SubProperties[0];
                    if (leafSegment.EpmInfo.IsAtomLinkMapping)
                    {
                        if (leafSegment.EpmInfo.CriteriaValue == null)
                        {
                            throw new ODataException(Strings.EpmSourceTree_MultiValueOfPrimitiveMappedToLinkWithoutCriteria(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                        else if (leafSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.LinkHref)
                        {
                            throw new ODataException(Strings.EpmTargetTree_ConditionalMappingLinkHrefIsRequired(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                    }
                    else if (leafSegment.EpmInfo.IsAtomCategoryMapping)
                    {
                        if (leafSegment.EpmInfo.CriteriaValue == null && leafSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CategoryTerm)
                        {
                            throw new ODataException(Strings.EpmTargetTree_ConditionalMappingCategoryTermIsRequired(
                                multiValuePropertyInfo.Attribute.SourcePath, 
                                multiValuePropertyInfo.DefiningType.Name, 
                                leafSegment.EpmInfo.Attribute.TargetPath));
                        }
                    }
                }
                else
                {
                    // MultiValue of complex types, we're on a leaf primitive property.
                    Debug.Assert(
                        multiValueSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueItemProperty,
                        "Only multiValue property or multiValue item property nodes should get here.");
                    Debug.Assert(multiValueSegment.SubProperties.Count == 0, "Primtive item property value should have no sub properties.");
                    Debug.Assert(!multiValueSegment.IsMultiValueItemValue, "Special MultiValue item value must not be a child of a complex property.");

                    EntityPropertyMappingInfo multiValueSegmentEpmInfo = multiValueSegment.EpmInfo;
                    if (multiValueSegmentEpmInfo.IsAtomLinkMapping != multiValueSegmentEpmInfo.IsAtomLinkMapping ||
                        multiValueSegmentEpmInfo.IsAtomCategoryMapping != multiValueSegmentEpmInfo.IsAtomCategoryMapping ||
                        String.Compare(multiValueSegmentEpmInfo.CriteriaValue, multiValueSegmentEpmInfo.CriteriaValue, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        throw new ODataException(Strings.EpmSourceTree_MultiValueOfComplexTypesDifferentConditionalMapping(
                            multiValuePropertyInfo.Attribute.SourcePath, 
                            multiValuePropertyInfo.DefiningType.Name,
                            multiValuePropertyInfo.Attribute.TargetPath));
                    }
                }
            }
        }
Пример #24
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");

            // We should never try to remove a multiValue item property from the target tree.
            // If derived type redefines mapping for a given multiValue, the multiValue node itself should be removed first and then all its new items
            //   should be added (but since the multiValue node, that is their parent, was replaces, there should be no collisions and thus no need to remove anything)
            Debug.Assert(epmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueItemProperty, "We should never try to remove a multiValue item property.");

            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) &&
                                                        segment.MatchCriteria(epmInfo.CriteriaValue, epmInfo.Criteria));
                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)
                {
                    if (currentSegment.EpmInfo.IsAtomLinkMapping || currentSegment.EpmInfo.IsAtomCategoryMapping)
                    {
                        this.countOfNonContentV3mappings--;
                    }
                    else
                    {
                        this.countOfNonContentV2mappings--;
                    }
                }

                EpmTargetPathSegment parentSegment = null;
                do
                {
                    // We should never be removing the multiValue property due to its children being removed
                    Debug.Assert(
                        currentSegment.EpmInfo == null || currentSegment.EpmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty || parentSegment == null,
                        "We should never be removing the multiValue property due to its child being removed. The source tree Add method should remove the multiValue property node itself first in that case.");

                    parentSegment = currentSegment.ParentSegment;
                    parentSegment.SubSegments.Remove(currentSegment);
                    currentSegment = parentSegment;
                }
                while (currentSegment.ParentSegment != null && !currentSegment.HasContent && currentSegment.SubSegments.Count == 0);
            }
        }
Пример #25
0
        /// <summary>
        /// Reads a property value starting with the specified index to the property value path.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param>
        /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
        /// <param name="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
        /// <param name="epmValueCache">The EPM value cache to use.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        private object ReadPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            IEnumerable <ODataProperty> cachedProperties,
            int sourceSegmentIndex,
            IEdmStructuredTypeReference structuredTypeReference,
            EpmValueCache epmValueCache)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index.");
            Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");

            EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string propertyName = sourceSegment.PropertyName;
            bool   lastSegment  = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1;

            IEdmStructuredType structuredType = structuredTypeReference.StructuredDefinition();
            IEdmProperty       edmProperty    = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType);

            if (edmProperty != null)
            {
                // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type
                if (lastSegment)
                {
                    if (!edmProperty.Type.IsODataPrimitiveTypeKind() && !edmProperty.Type.IsNonEntityODataCollectionTypeKind())
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else
                {
                    if (edmProperty.Type.TypeKind() != EdmTypeKind.Complex)
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                    }
                }
            }
            else
            {
                Debug.Assert(
                    structuredType.IsOpen,
                    "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
            }

            ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName);

            if (property == null)
            {
                throw new ODataException(o.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }

            object            propertyValue        = property.Value;
            ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue;

            if (lastSegment)
            {
                if (propertyValue == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(edmProperty, this.WriterBehavior, this.atomOutputContext.Model);
                }
                else
                {
                    // If this property is the last one it has to be either a primitive or collection
                    if (propertyComplexValue != null)
                    {
                        throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                    else
                    {
                        ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue;
                        if (propertyCollectionValue != null)
                        {
                            // Validate the type name for the collection
                            string typeName = propertyCollectionValue.TypeName;
                            WriterValidationUtils.ResolveTypeNameForWriting(
                                this.atomOutputContext.Model,
                                edmProperty == null ? null : edmProperty.Type,
                                ref typeName,
                                EdmTypeKind.Collection,
                                edmProperty == null);
                        }
                        else
                        {
                            if (propertyValue is ODataStreamReferenceValue)
                            {
                                // Stream properties should not come here, if it were an ODataEntry property it would have been
                                // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call.
                                throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                            }
                            else if (propertyValue is ISpatial)
                            {
                                throw new ODataException(o.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                            }
                            else if (edmProperty != null)
                            {
                                ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, edmProperty.Type);
                            }
                        }
                    }
                }

                return(propertyValue);
            }

            // Otherwise it's in the middle and thus it must be a complex value
            if (propertyComplexValue == null)
            {
                if (propertyValue != null)
                {
                    // It's not a complex value - fail.
                    throw new ODataException(o.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                else
                {
                    // The value of the property is null, which can be a null complex value
                    // Note that we must not attempt to resolve the type as if the type name was null here, because
                    //  1) We don't need the type for anything anyway (the value is null, this is the end)
                    //  2) If the property is open, trying to resolve a null type name would throw
                    //     but we don't have a null type name, we have a null entire value.
                    return(null);
                }
            }

            string localTypeName = propertyComplexValue.TypeName;
            IEdmComplexTypeReference complexValueType = WriterValidationUtils.ResolveTypeNameForWriting(
                this.atomOutputContext.Model,
                edmProperty == null ? null : edmProperty.Type,
                ref localTypeName,
                EdmTypeKind.Complex,
                edmProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(
                       epmInfo,
                       propertyComplexValue,
                       epmValueCache,
                       sourceSegmentIndex + 1,
                       complexValueType));
        }
Пример #26
0
        /// <summary>
        /// Override of the base Visitor method, which actually performs mapping search and serialization
        /// </summary>
        /// <param name="targetSegment">Current segment being checked for mapping</param>
        /// <param name="kind">Which sub segments to serialize</param>
        protected override void Serialize(EpmTargetPathSegment targetSegment, EpmSerializationKind kind)
        {
            if (targetSegment.HasContent)
            {
                EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;
                Object propertyValue;

                try
                {
                    propertyValue = epmInfo.ReadPropertyValue(this.Element);
                }
                catch (System.Reflection.TargetInvocationException)
                {
                    throw;
                }

                String          contentType;
                Action <String> contentWriter;

                switch (epmInfo.Attribute.TargetTextContentKind)
                {
                case SyndicationTextContentKind.Html:
                    contentType   = "html";
                    contentWriter = this.Target.WriteString;
                    break;

                case SyndicationTextContentKind.Xhtml:
                    contentType   = "xhtml";
                    contentWriter = this.Target.WriteRaw;
                    break;

                default:
                    contentType   = "text";
                    contentWriter = this.Target.WriteString;
                    break;
                }

                Action <String, bool, bool> textSyndicationWriter = (c, nonTextPossible, atomDateConstruct) =>
                {
                    this.Target.WriteStartElement(c, XmlConstants.AtomNamespace);
                    if (nonTextPossible)
                    {
                        this.Target.WriteAttributeString(XmlConstants.AtomTypeAttributeName, String.Empty, contentType);
                    }

                    // As per atomPub spec dateConstructs must contain valid datetime. Therefore we need to fill atom:updated/atom:published
                    // field with something (e.g. DateTimeOffset.MinValue) if the user wants to map null to either of these fields. This will
                    // satisfy protocol requirements and Syndication API. Note that the content will still contain information saying that the
                    // mapped property is supposed to be null - therefore the server will know that the actual value sent by the user is null.
                    // For all other elements we can use empty string since the content is not validated.
                    String textPropertyValue =
                        propertyValue != null?ClientConvert.ToString(propertyValue, atomDateConstruct) :
                            atomDateConstruct?ClientConvert.ToString(DateTime.MinValue, atomDateConstruct) :
                                String.Empty;

                    contentWriter(textPropertyValue);
                    this.Target.WriteEndElement();
                };

                switch (epmInfo.Attribute.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.ContributorEmail:
                    textSyndicationWriter(XmlConstants.AtomEmailElementName, false, false);
                    break;

                case SyndicationItemProperty.AuthorName:
                case SyndicationItemProperty.ContributorName:
                    textSyndicationWriter(XmlConstants.AtomNameElementName, false, false);
                    this.authorNamePresent = true;
                    break;

                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorUri:
                    textSyndicationWriter(XmlConstants.AtomUriElementName, false, false);
                    break;

                case SyndicationItemProperty.Updated:
                    textSyndicationWriter(XmlConstants.AtomUpdatedElementName, false, true);
                    this.updatedPresent = true;
                    break;

                case SyndicationItemProperty.Published:
                    textSyndicationWriter(XmlConstants.AtomPublishedElementName, false, true);
                    break;

                case SyndicationItemProperty.Rights:
                    textSyndicationWriter(XmlConstants.AtomRightsElementName, true, false);
                    break;

                case SyndicationItemProperty.Summary:
                    textSyndicationWriter(XmlConstants.AtomSummaryElementName, true, false);
                    break;

                case SyndicationItemProperty.Title:
                    textSyndicationWriter(XmlConstants.AtomTitleElementName, true, false);
                    break;

                default:
                    Debug.Assert(false, "Unhandled SyndicationItemProperty enum value - should never get here.");
                    break;
                }
            }
            else
            {
                if (targetSegment.SegmentName == XmlConstants.AtomAuthorElementName)
                {
                    this.CreateAuthor(false);
                    base.Serialize(targetSegment, kind);
                    this.FinishAuthor();
                }
                else if (targetSegment.SegmentName == XmlConstants.AtomContributorElementName)
                {
                    this.Target.WriteStartElement(XmlConstants.AtomContributorElementName, XmlConstants.AtomNamespace);
                    base.Serialize(targetSegment, kind);
                    this.Target.WriteEndElement();
                }
                else
                {
                    Debug.Assert(false, "Only authors and contributors have nested elements");
                }
            }
        }
Пример #27
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");

            String targetPath   = epmInfo.Attribute.TargetPath;
            String namespaceUri = epmInfo.Attribute.TargetNamespaceUri;

            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 InvalidOperationException(Strings.EpmTargetTree_InvalidTargetPath(targetPath));
                }

                if (targetSegment[0] == '@' && i != targetSegments.Length - 1)
                {
                    throw new InvalidOperationException(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, 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 ArgumentException(Strings.EpmTargetTree_DuplicateEpmAttrsWithSameTargetName(EpmTargetTree.GetPropertyNameFromEpmInfo(currentSegment.EpmInfo), currentSegment.EpmInfo.DefiningType.Name, 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. Since root has no ancestor, pass in false for ancestorHasContent
            if (EpmTargetTree.HasMixedContent(this.NonSyndicationRoot, false))
            {
                throw new InvalidOperationException(Strings.EpmTargetTree_InvalidTargetPath(targetPath));
            }
        }
 internal void Add(EntityPropertyMappingInfo epmInfo, string value)
 {
     this.customEpmValues.Add(new KeyValuePair <EntityPropertyMappingInfo, string>(epmInfo, value));
 }
Пример #29
0
 protected void SetEpmValue(IList targetList, IEdmTypeReference targetTypeReference, EntityPropertyMappingInfo epmInfo, object propertyValue)
 {
     this.SetEpmValueForSegment(epmInfo, 0, targetTypeReference.AsStructuredOrNull(), (List <ODataProperty>)targetList, propertyValue);
 }
 internal bool Contains(EntityPropertyMappingInfo epmInfo)
 {
     return(this.customEpmValues.Any <KeyValuePair <EntityPropertyMappingInfo, string> >(epmValue => object.ReferenceEquals(epmValue.Key, epmInfo)));
 }
Пример #31
0
 protected object ReadEntryPropertyValue(EntityPropertyMappingInfo epmInfo, EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType)
 {
     return(this.ReadPropertyValue(epmInfo, epmValueCache.EntryProperties, 0, entityType, epmValueCache));
 }
Пример #32
0
        private AtomEntryMetadata WriteEntryEpm(EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType)
        {
            EpmTargetPathSegment syndicationRoot = this.epmTargetTree.SyndicationRoot;

            if (syndicationRoot.SubSegments.Count == 0)
            {
                return(null);
            }
            foreach (EpmTargetPathSegment segment2 in syndicationRoot.SubSegments)
            {
                if (!segment2.HasContent)
                {
                    goto Label_018C;
                }
                EntityPropertyMappingInfo epmInfo = segment2.EpmInfo;
                object propertyValue       = base.ReadEntryPropertyValue(epmInfo, epmValueCache, entityType);
                string propertyValueAsText = EpmWriterUtils.GetPropertyValueAsText(propertyValue);
                switch (epmInfo.Attribute.TargetSyndicationItem)
                {
                case SyndicationItemProperty.Updated:
                {
                    if (base.WriterBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesClient)
                    {
                        break;
                    }
                    this.entryMetadata.UpdatedString = CreateDateTimeStringValue(propertyValue, base.WriterBehavior);
                    continue;
                }

                case SyndicationItemProperty.Published:
                {
                    if (base.WriterBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesClient)
                    {
                        goto Label_00FE;
                    }
                    this.entryMetadata.PublishedString = CreateDateTimeStringValue(propertyValue, base.WriterBehavior);
                    continue;
                }

                case SyndicationItemProperty.Rights:
                {
                    this.entryMetadata.Rights = CreateAtomTextConstruct(propertyValueAsText, epmInfo.Attribute.TargetTextContentKind);
                    continue;
                }

                case SyndicationItemProperty.Summary:
                {
                    this.entryMetadata.Summary = CreateAtomTextConstruct(propertyValueAsText, epmInfo.Attribute.TargetTextContentKind);
                    continue;
                }

                case SyndicationItemProperty.Title:
                {
                    this.entryMetadata.Title = CreateAtomTextConstruct(propertyValueAsText, epmInfo.Attribute.TargetTextContentKind);
                    continue;
                }

                default:
                    throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteEntryEpm_ContentTarget));
                }
                this.entryMetadata.Updated = new DateTimeOffset?(CreateDateTimeValue(propertyValue, SyndicationItemProperty.Updated, base.WriterBehavior));
                continue;
                Label_00FE:
                this.entryMetadata.Published = new DateTimeOffset?(CreateDateTimeValue(propertyValue, SyndicationItemProperty.Published, base.WriterBehavior));
                continue;
                Label_018C:
                this.WriteParentSegment(segment2, epmValueCache, entityType);
            }
            return(this.entryMetadata);
        }
Пример #33
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));
     }
 }
Пример #34
0
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();

            List<EpmSourcePathSegment> pathToCurrentSegment = new List<EpmSourcePathSegment>();
            EpmSourcePathSegment currentSourceSegment = this.Root;
            EpmSourcePathSegment foundSourceSegment = null;
            ResourceType currentType = epmInfo.ActualPropertyType;
            EpmSourcePathSegment multiValuePropertySegment = null;

            Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path");
            string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/');

            if (epmInfo.CriteriaValue != null)
            {
                ValidateConditionalMapping(epmInfo);
            }

            Debug.Assert(propertyPath.Length > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPath.Length; sourcePropertyIndex++)
            {
                string propertyName = propertyPath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new ODataException(Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, epmInfo.Attribute.SourcePath));
                }

                bool isMultiValueProperty;
                currentType = GetPropertyType(currentType, propertyName, out isMultiValueProperty);

                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }

                pathToCurrentSegment.Add(currentSourceSegment);

                if (isMultiValueProperty)
                {
                    Debug.Assert(
                        currentSourceSegment.EpmInfo == null || currentSourceSegment.EpmInfo.MultiValueStatus == EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                        "MultiValue property must have EpmInfo marked as MultiValue or none at all.");
                    Debug.Assert(
                        currentSourceSegment.EpmInfo != null || foundSourceSegment == null,
                        "The only way to get a propety without info attached yet on a MultiValue property is when we just created it.");

                    if (multiValuePropertySegment != null)
                    {
                        // Nested MultiValue - not allowed to be mapped
                        throw new ODataException(Strings.EpmSourceTree_NestedMultiValue(
                            multiValuePropertySegment.EpmInfo.Attribute.SourcePath,
                            multiValuePropertySegment.EpmInfo.DefiningType.Name,
                            epmInfo.Attribute.SourcePath));
                    }

                    multiValuePropertySegment = currentSourceSegment;

                    // MultiValue properties can only be mapped to a top-level element, so we can blindly use the first part
                    //   of the target path as the target path for the MultiValue property.
                    Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.TargetPath), "Target path should have been checked by the EpmAttribute constructor.");
                    string multiValuePropertyTargetPath = epmInfo.Attribute.TargetPath.Split('/')[0];

                    if (currentSourceSegment.EpmInfo == null || !currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                    {
                        if (currentSourceSegment.EpmInfo != null)
                        {
                            Debug.Assert(!currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "Just verifying that the ifs are correct.");
                            Debug.Assert(foundSourceSegment != null, "Can't have existing node with EpmInfo on it here and not found it before.");

                            // If the MultiValue property we're trying to add is from a different type than the one we already have
                            //   just overwrite the epm info. This means that the derived type defines a different mapping for the property than the base type.
                            //   We also need to walk all the children of the base type mapping here and remove them from the target tree
                            //   since we're overriding the entire MultiValue property mapping (not just one item property)
                            // Note that for MultiValue properties, removing the MultiValue property node itself will remove all the MultiValue item properties as well
                            //   as they have to be children of the MultiValue property node in the target tree.
                            this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);

                            // We also need to remove all children of the MultiValue property node from the source tree
                            //   as the derived type is overriding it completely. If the derived type doesn't override all of the properties
                            //   we should fail as if it did't map all of them.
                            currentSourceSegment.SubProperties.Clear();
                        }

                        // This is the first time we've seen this MultiValue property mapped
                        //   (on this type, we might have seen it on the base type, but that has been removed)

                        // The source path is the path we have so far for the property
                        string multiValuePropertySourcePath = string.Join("/", propertyPath, 0, sourcePropertyIndex + 1);

                        if (!epmInfo.IsSyndicationMapping)
                        {
                            // Custom EPM for MultiValue is not supported yet
                            // Note: This has already been implemented, but then removed from the code. To see what it takes to implement this
                            //   please see the change which adds this comment into the sources.
                            throw new ODataException(Strings.EpmSourceTree_MultiValueNotAllowedInCustomMapping(
                                multiValuePropertySourcePath,
                                epmInfo.DefiningType.Name));
                        }

                        // Create a new EPM attribute to represent the MultiValue property mapping
                        //   note that this attribute is basically implicitly declared whenever the user declares EPM attribute
                        //   for a property from some MultiValue property. (the declaration happens right here)
                        EntityPropertyMappingAttribute multiValueEpmAttribute = new EntityPropertyMappingAttribute(
                                multiValuePropertySourcePath,
                                multiValuePropertyTargetPath,
                                epmInfo.Attribute.TargetNamespacePrefix,
                                epmInfo.Attribute.TargetNamespaceUri,
                                epmInfo.Attribute.KeepInContent);

                        // Create a special EpmInfo from the above special attribute which represents just the MultiValue property itself
                        EntityPropertyMappingInfo multiValueEpmInfo = new EntityPropertyMappingInfo(
                            multiValueEpmAttribute, 
                            epmInfo.DefiningType, 
                            epmInfo.ActualPropertyType);
                        multiValueEpmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueProperty;
                        multiValueEpmInfo.MultiValueItemType = currentType;

                        // We need to mark the info as syndication/custom mapping explicitely since the attribute we create (From which it's infered) is always custom mapping
                        Debug.Assert(epmInfo.IsSyndicationMapping, "Only syndication mapping is allowed for MultiValue properties.");
                        multiValueEpmInfo.SetMultiValuePropertySyndicationMapping();

                        multiValueEpmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());

                        multiValueEpmInfo.Criteria = epmInfo.Criteria;
                        multiValueEpmInfo.CriteriaValue = epmInfo.CriteriaValue;

                        // Now associate the current source tree segment with the new info object (or override the one from base)
                        currentSourceSegment.EpmInfo = multiValueEpmInfo;

                        // And add the new info to the target tree
                        this.epmTargetTree.Add(multiValueEpmInfo);

                        // And continue with the walk of the source path.
                        // This means that the EPM attribute specified as the input to this method is still to be added
                        // It might be added to the source tree if the path is longer (property on an item in the MultiValue property of complex types)
                        //   or it might not be added to the source tree if this segment is the last (MultiValue property of primitive types).
                        // In any case it will be added to the target tree (so even if the MultiValue property itself is mapped to the top-level element only
                        //   the items in the MultiValue property can be mapped to child element/attribute of that top-level element).
                    }
                    else
                    {
                        Debug.Assert(currentSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo), "The condition in the surrounding if is broken.");

                        // We have already found a MultiValue property mapped from this source node.
                        // If it's on the same defining type we need to make sure that it's the same MultiValue property being mapped
                        // First verify that the mapping for the other property has the same top-level element for the MultiValue property
                        //   since we only allow properties from one MultiValue property to be mapped to the same top-level element
                        if (multiValuePropertyTargetPath != currentSourceSegment.EpmInfo.Attribute.TargetPath ||
                            epmInfo.Attribute.TargetNamespacePrefix != currentSourceSegment.EpmInfo.Attribute.TargetNamespacePrefix ||
                            epmInfo.Attribute.TargetNamespaceUri != currentSourceSegment.EpmInfo.Attribute.TargetNamespaceUri ||
                            epmInfo.Criteria != currentSourceSegment.EpmInfo.Criteria ||
                            String.Compare(epmInfo.Attribute.CriteriaValue, currentSourceSegment.EpmInfo.CriteriaValue, StringComparison.OrdinalIgnoreCase) != 0)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedToDifferentTopLevelElements(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }

                        // Second verify that the mappings for both properties have the same KeepInContent value
                        if (epmInfo.Attribute.KeepInContent != currentSourceSegment.EpmInfo.Attribute.KeepInContent)
                        {
                            throw new ODataException(Strings.EpmSourceTree_PropertiesFromSameMultiValueMappedWithDifferentKeepInContent(currentSourceSegment.EpmInfo.Attribute.SourcePath, currentSourceSegment.EpmInfo.DefiningType.Name));
                        }
                    }
                }
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            //   and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
                if (currentType.ResourceTypeKind != ResourceTypeKind.Primitive)
                {
                    throw new ODataException(Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }

                SyndicationItemProperty targetSyndicationItem = epmInfo.Attribute.TargetSyndicationItem;
                if (targetSyndicationItem == SyndicationItemProperty.LinkRel || targetSyndicationItem == SyndicationItemProperty.CategoryScheme)
                {
                    if (PrimitiveStringResourceType != currentType)
                    {
                        throw new InvalidOperationException(Strings.EpmSourceTree_NonStringPropertyMappedToConditionAttribute(
                            currentSourceSegment.PropertyName,
                            epmInfo.DefiningType.FullName,
                            targetSyndicationItem.ToString()));
                    }
                }
            }

            if (multiValuePropertySegment == currentSourceSegment)
            {
                // If the MultiValue property is the last segment it means that the MultiValue property itself is being mapped (and it must be a MultiValue of primitive types).

                // If we found the MultiValue property already in the tree, here we actually want the item of the MultiValue property (as the MultiValue one was processed above already)
                // If we have the item value already in the tree use it as the foundProperty so that we correctly check the duplicate mappings below
                if (foundSourceSegment != null)
                {
                    Debug.Assert(foundSourceSegment == currentSourceSegment, "If we found an existing segment it must be the current one.");
                    foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.IsMultiValueItemValue);
                }

                if (foundSourceSegment == null)
                {
                    // This is a bit of a special case. In the source tree we will create a special node to represent the item value (we need that to be able to tell
                    //   if it was not mapped twice).
                    // In the target tree, we will also create a special node which will hold the information specific
                    //   to serialization of the item value (for example the exact syndication mapping target and so on).
                    //   The creation of the special node is done in the target tree Add method.
                    EpmSourcePathSegment newSegment = EpmSourcePathSegment.CreateMultiValueItemValueSegment();
                    currentSourceSegment.SubProperties.Add(newSegment);
                    currentSourceSegment = newSegment;
                }
                else
                {
                    currentSourceSegment = foundSourceSegment;
                }
            }

            // Note that once we're here the EpmInfo we have is never the MultiValue property itself, it's always either a non-MultiValue property
            //   or MultiValue item property.
            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same ResourceType, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(Object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");
                Debug.Assert(
                    foundSourceSegment.EpmInfo.MultiValueStatus != EntityPropertyMappingMultiValueStatus.MultiValueProperty,
                    "We should never get here with a MultiValue property itself, we should have a node represent its item or property on the item instead.");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new ODataException(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            epmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());
            currentSourceSegment.EpmInfo = epmInfo;

            if (multiValuePropertySegment != null)
            {
                Debug.Assert(multiValuePropertySegment.EpmInfo != null, "All MultiValue property segments must have EpmInfo assigned.");

                // We are mapping a MultiValue property - so mark the info as a MultiValue item property (since the MultiValue property itself was added above)
                epmInfo.MultiValueStatus = EntityPropertyMappingMultiValueStatus.MultiValueItemProperty;

                // Set the item type on each of the item properties, so that the segmented path know from which type to start
                epmInfo.MultiValueItemType = multiValuePropertySegment.EpmInfo.MultiValueItemType;

                // And trim its property value path to start from the MultiValue item. This path is basically a list of properties to traverse
                //   when access the value of the property on the specified resource. For non-MultiValue and MultiValue properties themselves 
                //   this path starts with the entity instance. For MultiValue item properties this path starts with the MultiValue item instance.
                //   Note that if it's a MultiValue of primitive types, the path is going to be empty meaning that the value is the item instance itself.
                epmInfo.TrimMultiValueItemPropertyPath(multiValuePropertySegment.EpmInfo);

#if DEBUG
                // Check that if the MultiValue item is of primitive type, we can only ever add a single child source segment which points directly to the MultiValue property itself
                // If we would allow this here, we would fail later, but with a much weirder error message
                Debug.Assert(
                    multiValuePropertySegment.EpmInfo.MultiValueItemType.ResourceTypeKind != ResourceTypeKind.Primitive || epmInfo.PropertyValuePath.Length == 0,
                    "We shoud have failed to map a subproperty of a primitive MultiValue item.");
#endif
            }

            this.epmTargetTree.Add(epmInfo);
        }
Пример #35
0
        /// <summary>
        /// Adds a path to the source and target tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/>
        /// </summary>
        /// <param name="epmInfo">EnitityPropertyMappingInfo holding the source path</param>
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            DebugUtils.CheckNoExternalCallers();

            List<EpmSourcePathSegment> pathToCurrentSegment = new List<EpmSourcePathSegment>();
            EpmSourcePathSegment currentSourceSegment = this.Root;
            EpmSourcePathSegment foundSourceSegment = null;
            IEdmType currentType = epmInfo.ActualPropertyType;

            Debug.Assert(!string.IsNullOrEmpty(epmInfo.Attribute.SourcePath), "Invalid source path");
            string[] propertyPath = epmInfo.Attribute.SourcePath.Split('/');

            int propertyPathLength = propertyPath.Length;
            Debug.Assert(propertyPathLength > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < propertyPathLength; sourcePropertyIndex++)
            {
                string propertyName = propertyPath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new ODataException(o.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath));
                }

                IEdmTypeReference nextPropertyTypeReference = GetPropertyType(currentType, propertyName);
                IEdmType nextPropertyType = nextPropertyTypeReference == null ? null : nextPropertyTypeReference.Definition;

                // If we don't find a property type this is an open property; check whether this is the last segment in the path
                // since otherwise this would not be an open primitive property and only open primitive properties are allowed.
                if (nextPropertyType == null && sourcePropertyIndex < propertyPathLength - 1)
                {
                    throw new ODataException(o.Strings.EpmSourceTree_OpenComplexPropertyCannotBeMapped(propertyName, currentType.ODataFullName()));
                }

                currentType = nextPropertyType;
                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }

                pathToCurrentSegment.Add(currentSourceSegment);
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            // and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
                if (!currentType.IsODataPrimitiveTypeKind())
                {
                    throw new ODataException(o.Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }
            }

            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same type, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new ODataException(o.Strings.EpmSourceTree_DuplicateEpmAttributesWithSameSourceName(epmInfo.DefiningType.ODataFullName(), epmInfo.Attribute.SourcePath));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            epmInfo.SetPropertyValuePath(pathToCurrentSegment.ToArray());
            currentSourceSegment.EpmInfo = epmInfo;

            this.epmTargetTree.Add(epmInfo);
        }
Пример #36
0
        /// <summary>
        /// Validates conditional mapping.
        /// </summary>
        /// <param name="epmInfo">Epm mapping info</param>
        private static void ValidateConditionalMapping(EntityPropertyMappingInfo epmInfo)
        {
            Debug.Assert(epmInfo.CriteriaValue != null, "epmInfo.CriteriaValue != null");

            String criteriaValue = epmInfo.CriteriaValue;
            if (epmInfo.Criteria == EpmSyndicationCriteria.LinkRel && !EntityPropertyMappingInfo.IsValidLinkRelCriteriaValue(criteriaValue))
            {
                throw new ODataException(Strings.EpmSourceTree_ConditionalMappingInvalidLinkRelCriteriaValue(
                    criteriaValue,
                    epmInfo.Attribute.SourcePath, 
                    epmInfo.DefiningType.Name));
            }

            if (epmInfo.Criteria == EpmSyndicationCriteria.CategoryScheme && !EntityPropertyMappingInfo.IsValidCategorySchemeCriteriaValue(criteriaValue))
            {
                throw new ODataException(Strings.EpmSourceTree_ConditionalMappingInvalidCategorySchemeCriteriaValue(
                    criteriaValue,
                    epmInfo.Attribute.SourcePath, 
                    epmInfo.DefiningType.Name));
            }

            if (epmInfo.IsAtomLinkMapping)
            {
                if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.LinkRel)
                {
                    throw new ODataException(Strings.EpmTargetTree_ConditionalMappingToCriteriaAttribute(
                        epmInfo.Attribute.SourcePath, 
                        epmInfo.DefiningType.Name,
                        epmInfo.Attribute.TargetPath));
                }
            }
            else if (epmInfo.IsAtomCategoryMapping)
            {
                if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CategoryScheme)
                {
                    throw new ODataException(Strings.EpmTargetTree_ConditionalMappingToCriteriaAttribute(
                        epmInfo.Attribute.SourcePath, 
                        epmInfo.DefiningType.Name,
                        epmInfo.Attribute.TargetPath));
                }
            }
            else
            {
                throw new ODataException(Strings.EpmTargetTree_ConditionalMappingToNonConditionalSyndicationItem(
                    epmInfo.Attribute.SourcePath, 
                    epmInfo.DefiningType.Name,
                    epmInfo.Attribute.TargetPath));
            }
        }
Пример #37
0
        private object ReadPropertyValue(EntityPropertyMappingInfo epmInfo, IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache)
        {
            EpmSourcePathSegment segment            = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string             propertyName         = segment.PropertyName;
            bool               flag                 = epmInfo.PropertyValuePath.Length == (sourceSegmentIndex + 1);
            IEdmStructuredType owningStructuredType = structuredTypeReference.StructuredDefinition();
            IEdmProperty       expectedProperty     = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningStructuredType);

            if (expectedProperty != null)
            {
                if (flag)
                {
                    if (!expectedProperty.Type.IsODataPrimitiveTypeKind() && !expectedProperty.Type.IsNonEntityODataCollectionTypeKind())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else if (expectedProperty.Type.TypeKind() != EdmTypeKind.Complex)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
            }
            ODataProperty property2 = (cachedProperties == null) ? null : cachedProperties.FirstOrDefault <ODataProperty>(p => (p.Name == propertyName));

            if (property2 == null)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }
            object            obj2         = property2.Value;
            ODataComplexValue complexValue = obj2 as ODataComplexValue;

            if (flag)
            {
                if (obj2 == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, this.WriterBehavior, this.atomOutputContext.Model);
                    return(obj2);
                }
                if (complexValue != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                }
                ODataCollectionValue value3 = obj2 as ODataCollectionValue;
                if (value3 != null)
                {
                    string str = value3.TypeName;
                    WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref str, EdmTypeKind.Collection, expectedProperty == null);
                    return(obj2);
                }
                if (obj2 is ODataStreamReferenceValue)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }
                if (obj2 is ISpatial)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                }
                if (expectedProperty != null)
                {
                    ValidationUtils.ValidateIsExpectedPrimitiveType(obj2, expectedProperty.Type);
                }
                return(obj2);
            }
            if (complexValue == null)
            {
                if (obj2 != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                return(null);
            }
            string typeName = complexValue.TypeName;
            IEdmComplexTypeReference complexType = WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref typeName, EdmTypeKind.Complex, expectedProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(epmInfo, complexValue, epmValueCache, sourceSegmentIndex + 1, complexType));
        }
            /// <summary>Adds a property to the null valued collection</summary>
            /// <param name="epmInfo">EpmInfo containing the property information such as path</param>
            internal void Add(EntityPropertyMappingInfo epmInfo)
            {
                Debug.Assert(epmInfo != null, "epmInfo != null");
                EpmNullValuedPropertyNode current = this.root;
                ResourceType currentType = epmInfo.DefiningType;
                object currentValue = this.element;

                // We are here because the epm path points to a null value. If the path is multiple level deep, we need to
                // know the first level the null value begins and we don't need to serialize deeper than that.
                // To serialize the complex properties correctly in the case they are not already in content, we also need
                // to find the type for each segment from root to the first segment that has the null property value.
                foreach (var segment in epmInfo.Attribute.SourcePath.Split('/'))
                {
                    EpmNullValuedPropertyNode child = current.Children.FirstOrDefault(c => c.Name == segment);
                    if (child != null)
                    {
                        // The current segment is already added to the tree, reuse it.
                        current = child;
                        currentValue = child.Element;
                        currentType = child.ResourceType;
                    }
                    else
                    {
                        EpmNullValuedPropertyNode newNode = new EpmNullValuedPropertyNode { Name = segment };
                        Debug.Assert(currentType != null, "currentType != null");
                        ResourceProperty property = currentType.TryResolvePropertyName(segment);

                        Debug.Assert(currentValue != null, "currentValue != null");
                        ProjectedWrapper projectedValue = currentValue as ProjectedWrapper;
                        if (projectedValue == null)
                        {
                            if (property != null)
                            {
                                currentValue = this.provider.GetPropertyValue(currentValue, property, currentType);
                                currentValue = currentValue == DBNull.Value ? null : currentValue;
                                currentType = property.ResourceType;
                            }
                            else
                            {
                                // Handle open property...
                                currentValue = this.provider.GetOpenPropertyValue(currentValue, segment);
                                currentValue = currentValue == DBNull.Value ? null : currentValue;
                                if (currentValue != null)
                                {
                                    // Get the type from the instance.
                                    currentType = this.provider.GetResourceType(currentValue);
                                }
                                else
                                {
                                    // We have a null open property at hand, we don't know its type.
                                    // Default the type to string so that we will omit the type name
                                    // and just write out null. i.e. <d:prop m:null='true'/>
                                    currentType = ResourceType.PrimitiveStringResourceType;
                                }
                            }
                        }
                        else
                        {
                            currentValue = projectedValue.GetProjectedPropertyValue(segment);
                            currentValue = currentValue == DBNull.Value ? null : currentValue;

                            if (property != null)
                            {
                                currentType = property.ResourceType;
                            }
                            else
                            {
                                // Handle open property...
                                if (currentValue == null)
                                {
                                    // We have a null open property at hand, we don't know its type.
                                    // Default the type to string so that we will omit the type name
                                    // and just write out null. i.e. <d:prop m:null='true'/>
                                    currentType = ResourceType.PrimitiveStringResourceType;
                                }
                                else
                                {
                                    projectedValue = currentValue as ProjectedWrapper;
                                    if (projectedValue != null)
                                    {
                                        // Get the type from the project wrapper.
                                        currentType = this.provider.TryResolveResourceType(projectedValue.ResourceTypeName);
                                    }
                                    else
                                    {
                                        // Get the type from the instance.
                                        currentType = this.provider.GetResourceType(currentValue);
                                    }
                                }
                            }
                        }

                        Debug.Assert(currentType != null, "currentType != null");
                        Debug.Assert(currentValue != DBNull.Value, "currentValue != DBNull.Value -- we have converted DBNull to null");
                        newNode.ResourceType = currentType;
                        newNode.Element = currentValue;
                        current.Children.Add(newNode);
                        current = newNode;
                    }

                    if (current.Element == null)
                    {
                        // If the current element is null, we don't need to go further since that is the obvious reason
                        // that the children properties are null.
                        break;
                    }
                }
            }
Пример #39
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));
            }
        }
Пример #40
0
 /// <summary>Compares the defining type of this info and other EpmInfo object.</summary>
 /// <param name="other">The other EpmInfo object to compare to.</param>
 /// <returns>true if the defining types are the same</returns>
 internal bool DefiningTypesAreEqual(EntityPropertyMappingInfo other)
 {
     return(this.DefiningType == other.DefiningType);
 }
Пример #41
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);
            }
        }
Пример #42
0
        /// <summary>
        /// Given an <see cref="EntityPropertyMappingInfo"/> gives the correct target path for it
        /// </summary>
        /// <param name="epmInfo">Given <see cref="EntityPropertyMappingInfo"/></param>
        /// <returns>string with the correct value for the target path</returns>
        private static string GetPropertyNameFromEpmInfo(EntityPropertyMappingInfo epmInfo)
        {
            if (epmInfo.Attribute.TargetSyndicationItem == SyndicationItemProperty.CustomProperty)
            {
                return epmInfo.Attribute.TargetPath;
            }

            // for EF provider we want to return a name that corresponds to attribute in the edmx file while for CLR provider 
            // and the client we want to return a name that corresponds to the enum value used in EntityPropertyMapping attribute.
            return 
#if ASTORIA_SERVER                
                epmInfo.IsEFProvider ? EpmTranslate.MapSyndicationPropertyToEpmTargetPath(epmInfo.Attribute.TargetSyndicationItem) :
#endif
                epmInfo.Attribute.TargetSyndicationItem.ToString();
        }
Пример #43
0
 internal void Add(EntityPropertyMappingInfo epmInfo)
 {
Пример #44
0
        /// <summary>
        /// Writes the syndication part of EPM for an entry into ATOM metadata OM.
        /// </summary>
        /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param>
        /// <param name="entityType">The type of the entry.</param>
        /// <returns>The ATOM metadata OM with the EPM values populated.</returns>
        private AtomEntryMetadata WriteEntryEpm(
            EntryPropertiesValueCache epmValueCache,
            IEdmEntityTypeReference entityType)
        {
            // If there are no syndication mappings, just return null.
            EpmTargetPathSegment syndicationRootSegment = this.epmTargetTree.SyndicationRoot;

            Debug.Assert(syndicationRootSegment != null, "EPM Target tree must always have syndication root.");
            if (syndicationRootSegment.SubSegments.Count == 0)
            {
                return(null);
            }

            foreach (EpmTargetPathSegment targetSegment in syndicationRootSegment.SubSegments)
            {
                if (targetSegment.HasContent)
                {
                    EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo;
                    Debug.Assert(
                        epmInfo != null && epmInfo.Attribute != null,
                        "If the segment has content it must have EpmInfo which in turn must have the EPM attribute");

                    object propertyValue = this.ReadEntryPropertyValue(
                        epmInfo,
                        epmValueCache,
                        entityType);
                    string textPropertyValue = EpmWriterUtils.GetPropertyValueAsText(propertyValue);

                    switch (epmInfo.Attribute.TargetSyndicationItem)
                    {
                    case SyndicationItemProperty.Updated:
                        if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                        {
                            this.entryMetadata.UpdatedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior);
                        }
                        else
                        {
                            this.entryMetadata.Updated = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Updated, this.WriterBehavior);
                        }

                        break;

                    case SyndicationItemProperty.Published:
                        if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient)
                        {
                            this.entryMetadata.PublishedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior);
                        }
                        else
                        {
                            this.entryMetadata.Published = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Published, this.WriterBehavior);
                        }

                        break;

                    case SyndicationItemProperty.Rights:
                        this.entryMetadata.Rights = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    case SyndicationItemProperty.Summary:
                        this.entryMetadata.Summary = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    case SyndicationItemProperty.Title:
                        this.entryMetadata.Title = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind);
                        break;

                    default:
                        throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteEntryEpm_ContentTarget));
                    }
                }
                else
                {
                    this.WriteParentSegment(targetSegment, epmValueCache, entityType);
                }
            }

            return(this.entryMetadata);
        }
Пример #45
0
        internal void Add(EntityPropertyMappingInfo epmInfo, IEnumerable <ResourceProperty> declaredProperties)
        {
            Dictionary <ResourceType, IEnumerable <ResourceProperty> > declaredPropertiesLookup = new Dictionary <ResourceType, IEnumerable <ResourceProperty> >(EqualityComparer <ResourceType> .Default);

            declaredPropertiesLookup.Add(epmInfo.ActualPropertyType, declaredProperties);
#endif
            EpmSourcePathSegment           currentSourceSegment = this.Root;
            EpmSourcePathSegment           foundSourceSegment   = null;
            ClientTypeOrResourceType_Alias currentType          = epmInfo.ActualPropertyType;

            Debug.Assert(epmInfo.PropertyValuePath != null && epmInfo.PropertyValuePath.Length > 0, "Must have been validated during EntityPropertyMappingAttribute construction");
            for (int sourcePropertyIndex = 0; sourcePropertyIndex < epmInfo.PropertyValuePath.Length; sourcePropertyIndex++)
            {
                string propertyName = epmInfo.PropertyValuePath[sourcePropertyIndex];

                if (propertyName.Length == 0)
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, epmInfo.Attribute.SourcePath));
                }

#if ASTORIA_CLIENT
                currentType = GetPropertyType(currentType, propertyName);
#else
                currentType = GetPropertyType(currentType, propertyName, declaredPropertiesLookup);
#endif

                foundSourceSegment = currentSourceSegment.SubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundSourceSegment != null)
                {
                    currentSourceSegment = foundSourceSegment;
                }
                else
                {
                    EpmSourcePathSegment newSourceSegment = new EpmSourcePathSegment(propertyName);
                    currentSourceSegment.SubProperties.Add(newSourceSegment);
                    currentSourceSegment = newSourceSegment;
                }
            }

            // The last segment is the one being mapped from by the user specified attribute.
            // It must be a primitive type - we don't allow mappings of anything else than primitive properties directly.
            // Note that we can only verify this for non-open properties, for open properties we must assume it's a primitive type
            //   and we will make this check later during serialization again when we actually have the value of the property.
            if (currentType != null)
            {
#if ASTORIA_CLIENT
                if (!PrimitiveType.IsKnownNullableType(currentType.ElementType))
#else
                if (currentType.ResourceTypeKind != ResourceTypeKind.Primitive)
#endif
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_EndsWithNonPrimitiveType(currentSourceSegment.PropertyName));
                }
            }

            // Note that once we're here the EpmInfo we have is never the collection property itself, it's always either a non-collection property
            //   or collection item property.
            Debug.Assert(foundSourceSegment == null || foundSourceSegment.EpmInfo != null, "Can't have a leaf node in the tree without EpmInfo.");

            // Two EpmAttributes with same PropertyName in the same ResourceType, this could be a result of inheritance
            if (foundSourceSegment != null)
            {
                Debug.Assert(Object.ReferenceEquals(foundSourceSegment, currentSourceSegment), "currentSourceSegment variable should have been updated already to foundSourceSegment");

                // Check for duplicates on the same entity type
                Debug.Assert(foundSourceSegment.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundSourceSegment.EpmInfo.DefiningTypesAreEqual(epmInfo))
                {
                    throw new InvalidOperationException(c.Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }

                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundSourceSegment.EpmInfo);
            }

            currentSourceSegment.EpmInfo = epmInfo;

            this.epmTargetTree.Add(epmInfo);
        }
Пример #46
0
        private void SetEpmValueForSegment(EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, List <ODataProperty> existingProperties, object propertyValue)
        {
            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            if (!epmInfo.Attribute.KeepInContent)
            {
                IEdmTypeReference type;
                ODataProperty     property = existingProperties.FirstOrDefault <ODataProperty>(p => string.CompareOrdinal(p.Name, propertyName) == 0);
                ODataComplexValue value2   = null;
                if (property != null)
                {
                    value2 = property.Value as ODataComplexValue;
                    if (value2 == null)
                    {
                        return;
                    }
                }
                IEdmProperty property2 = segmentStructuralTypeReference.FindProperty(propertyName);
                if ((property2 == null) && (propertyValuePathIndex != (epmInfo.PropertyValuePath.Length - 1)))
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
                }
                if ((property2 == null) || (this.MessageReaderSettings.DisablePrimitiveTypeConversion && property2.Type.IsODataPrimitiveTypeKind()))
                {
                    type = EdmCoreModel.Instance.GetString(true);
                }
                else
                {
                    type = property2.Type;
                }
                switch (type.TypeKind())
                {
                case EdmTypeKind.Primitive:
                    object obj2;
                    if (type.IsStream())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                    }
                    if (propertyValue == null)
                    {
                        ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, type, this.atomInputContext.MessageReaderSettings, true, this.atomInputContext.Version);
                        obj2 = null;
                    }
                    else
                    {
                        obj2 = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, type.AsPrimitive());
                    }
                    this.AddEpmPropertyValue(existingProperties, propertyName, obj2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;

                case EdmTypeKind.Complex:
                {
                    if (value2 == null)
                    {
                        value2 = new ODataComplexValue {
                            TypeName   = type.ODataFullName(),
                            Properties = new ReadOnlyEnumerable <ODataProperty>()
                        };
                        this.AddEpmPropertyValue(existingProperties, propertyName, value2, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    }
                    IEdmComplexTypeReference reference2 = type.AsComplex();
                    this.SetEpmValueForSegment(epmInfo, propertyValuePathIndex + 1, reference2, ReaderUtils.GetPropertiesList(value2.Properties), propertyValue);
                    return;
                }

                case EdmTypeKind.Collection:
                {
                    ODataCollectionValue value4 = new ODataCollectionValue {
                        TypeName = type.ODataFullName(),
                        Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                    };
                    this.AddEpmPropertyValue(existingProperties, propertyName, value4, segmentStructuralTypeReference.IsODataEntityTypeKind());
                    return;
                }
                }
                throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
Пример #47
0
 private object ReadComplexPropertyValue(EntityPropertyMappingInfo epmInfo, ODataComplexValue complexValue, EpmValueCache epmValueCache, int sourceSegmentIndex, IEdmComplexTypeReference complexType)
 {
     return(this.ReadPropertyValue(epmInfo, EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, false), sourceSegmentIndex, complexType, epmValueCache));
 }