public void GetOrCreateReadOnlyEnumerableShouldGetForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable <int>(); var enumerable2 = enumerable.GetOrCreateReadOnlyEnumerable("Integers"); enumerable2.As <object>().Should().NotBeSameAs(ReadOnlyEnumerable <int> .Empty()); enumerable2.As <object>().Should().BeSameAs(enumerable); }
public void ConcatToReadOnlyEnumerableShouldAddForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable <ODataProperty>(); enumerable.Count().Should().Be(0); enumerable.ConcatToReadOnlyEnumerable("Properties", new ODataProperty()); enumerable.Count().Should().Be(1); }
public void ConcatToReadOnlyEnumerableShouldAddForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable <ODataProperty>(); Assert.Empty(enumerable); enumerable.ConcatToReadOnlyEnumerable("Properties", new ODataProperty()); Assert.Single(enumerable); }
public void GetOrCreateReadOnlyEnumerableShouldGetForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable <int>(); var enumerable2 = enumerable.GetOrCreateReadOnlyEnumerable("Integers"); Assert.NotSame(enumerable2, ReadOnlyEnumerable <int> .Empty()); Assert.Same(enumerable2, enumerable); }
public void AddToSourceListShouldAddToTheSourceList() { ReadOnlyEnumerable<int> enumerable = new ReadOnlyEnumerable<int>(); enumerable.Should().BeEmpty(); enumerable.AddToSourceList(1); enumerable.Count().Should().Be(1); enumerable.Should().OnlyContain(i => i == 1); }
/// <summary> /// Reads the content of a properties in an element (complex value, m:properties, ...) /// </summary> /// <param name="structuredType">The type which should declare the properties to be read. Optional.</param> /// <param name="properties">The list of properties to add properties to.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The element to read properties from. /// Post-Condition: XmlNodeType.Element - The element to read properties from if it is an empty element. /// XmlNodeType.EndElement - The end element of the element to read properties from. /// </remarks> protected void ReadProperties(IEdmStructuredType structuredType, ReadOnlyEnumerable <ODataProperty> properties, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.AssertRecursionDepthIsZero(); this.ReadPropertiesImplementation( structuredType, properties, duplicatePropertyNamesChecker); this.AssertRecursionDepthIsZero(); }
public void ConcatToReadOnlyEnumerableShouldCreateReadOnlyEnumerableAndAddForEmptySource() { var enumerable = ReadOnlyEnumerable <ODataProperty> .Empty(); var enumerable2 = enumerable.ConcatToReadOnlyEnumerable("Properties", new ODataProperty()); enumerable2.As <object>().Should().NotBeSameAs(enumerable); enumerable2.Count().Should().Be(1); }
public void ConcatToReadOnlyEnumerableShouldCreateReadOnlyEnumerableAndAddForEmptySource() { var enumerable = ReadOnlyEnumerable <ODataProperty> .Empty(); var enumerable2 = enumerable.ConcatToReadOnlyEnumerable("Properties", new ODataProperty()); Assert.NotSame(enumerable2, enumerable); Assert.Single(enumerable2); }
public void GetOrCreateReadOnlyEnumerableShouldCreateForEmptyReadOnlyEnumerableSource() { var enumerable = ReadOnlyEnumerable <int> .Empty(); var enumerable2 = enumerable.GetOrCreateReadOnlyEnumerable("Integers"); Assert.NotSame(enumerable2, ReadOnlyEnumerable <int> .Empty()); Assert.NotNull(enumerable2); }
public void AddToSourceListShouldAddToTheSourceList() { ReadOnlyEnumerable <int> enumerable = new ReadOnlyEnumerable <int>(); enumerable.Should().BeEmpty(); enumerable.AddToSourceList(1); enumerable.Count().Should().Be(1); enumerable.Should().OnlyContain(i => i == 1); }
public void AddToSourceListShouldAddToTheSourceList() { ReadOnlyEnumerable <int> enumerable = new ReadOnlyEnumerable <int>(); Assert.Empty(enumerable); enumerable.AddToSourceList(1); int value = Assert.Single(enumerable); Assert.Equal(1, value); }
/// <summary> /// Reads EPM values from a person construct (author or contributor). /// </summary> /// <param name="targetList">The target list, this can be either a list of properties (on entry or complex value), /// or a list of items (for a collection of primitive types).</param> /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity, complex or primitive).</param> /// <param name="targetSegment">The target segment which points to either author or contributor element.</param> /// <param name="personMetadata">The person ATOM metadata to read from.</param> private void ReadPersonEpm(ReadOnlyEnumerable <ODataProperty> targetList, IEdmTypeReference targetTypeReference, EpmTargetPathSegment targetSegment, AtomPersonMetadata personMetadata) { Debug.Assert(targetList != null, "targetList != null"); Debug.Assert(targetTypeReference != null, "targetTypeReference != null"); Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert( targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName, "targetSegment must be author or contributor."); Debug.Assert(personMetadata != null, "personMetadata != null"); foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author."); Debug.Assert( subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty, "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM."); switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.AuthorName: case SyndicationItemProperty.ContributorName: // Note that person name can never specify true null in EPM, since it can't have the m:null attribute on it. string personName = personMetadata.Name; if (personName != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personName); } break; case SyndicationItemProperty.AuthorEmail: case SyndicationItemProperty.ContributorEmail: string personEmail = personMetadata.Email; if (personEmail != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personEmail); } break; case SyndicationItemProperty.AuthorUri: case SyndicationItemProperty.ContributorUri: string personUri = personMetadata.UriFromEpm; if (personUri != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personUri); } break; default: // Unhandled EpmTargetPathSegment.SegmentName. throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadPersonEpm)); } } }
/// <summary> /// Read a complex value from the reader. /// </summary> /// <param name="complexTypeReference">The type reference of the value to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the complex value (possibly null).</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param> /// <param name="epmPresent">Whether any EPM mappings exist.</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.EndElement - the element has been read. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataComplexValue ReadComplexValue( IEdmComplexTypeReference complexTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool epmPresent) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); this.IncreaseRecursionDepth(); ODataComplexValue complexValue = new ODataComplexValue(); IEdmComplexType complexType = complexTypeReference == null ? null : (IEdmComplexType)complexTypeReference.Definition; // If we have a metadata type for the complex value, use that type name // otherwise use the type name from the payload (if there was any). complexValue.TypeName = complexType == null ? payloadTypeName : complexType.ODataFullName(); if (serializationTypeNameAnnotation != null) { complexValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } else { duplicatePropertyNamesChecker.Clear(); } ReadOnlyEnumerable <ODataProperty> properties = new ReadOnlyEnumerable <ODataProperty>(); this.ReadPropertiesImplementation(complexType, properties, duplicatePropertyNamesChecker, epmPresent); complexValue.Properties = properties; this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(complexValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return(complexValue); }
/// <summary> /// Creates and adds a new property to the list of properties for an EPM. /// </summary> /// <param name="properties">The list of properties to add the property to.</param> /// <param name="propertyName">The name of the property to add.</param> /// <param name="propertyValue">The value of the property to add.</param> /// <param name="checkDuplicateEntryPropertyNames">true if the new property should be checked for duplicates against the entry properties; false otherwise. /// This should be true if the <paramref name="properties"/> is the list of properties for the entry, and false in all other cases.</param> private void AddEpmPropertyValue(ReadOnlyEnumerable <ODataProperty> properties, string propertyName, object propertyValue, bool checkDuplicateEntryPropertyNames) { Debug.Assert(properties != null, "properties != null"); Debug.Assert(!string.IsNullOrEmpty(propertyName), "propertyName must not be null or empty."); // Create a new property object and add it. ODataProperty property = new ODataProperty { Name = propertyName, Value = propertyValue }; if (checkDuplicateEntryPropertyNames) { this.entryState.DuplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); } properties.AddToSourceList(property); }
/// <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( ReadOnlyEnumerable <ODataProperty> 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(), targetList, propertyValue); }
/// <summary> /// Full constructor. /// </summary> /// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/> implementation, used to send and receive SSDP network messages.</param> /// <param name="osName">Then name of the operating system running the server.</param> /// <param name="osVersion">The version of the operating system running the server.</param> /// <param name="log">An implementation of <see cref="ISsdpLogger"/> to be used for logging activity. May be null, in which case no logging is performed.</param> protected SsdpDevicePublisherBase(ISsdpCommunicationsServer communicationsServer, string osName, string osVersion, ISsdpLogger log) { if (communicationsServer == null) { throw new ArgumentNullException("communicationsServer"); } if (osName == null) { throw new ArgumentNullException("osName"); } if (osName.Length == 0) { throw new ArgumentException("osName cannot be an empty string.", "osName"); } if (osVersion == null) { throw new ArgumentNullException("osVersion"); } if (osVersion.Length == 0) { throw new ArgumentException("osVersion cannot be an empty string.", "osName"); } _Log = log ?? NullLogger.Instance; _SupportPnpRootDevice = true; _Devices = new List <SsdpRootDevice>(); _ReadOnlyDevices = new ReadOnlyEnumerable <SsdpRootDevice>(_Devices); _RecentSearchRequests = new Dictionary <string, SearchRequest>(StringComparer.OrdinalIgnoreCase); _Random = new Random(); _DeviceValidator = new Upnp10DeviceValidator(); //Should probably inject this later, but for now we only support 1.0. _CommsServer = communicationsServer; _CommsServer.RequestReceived += CommsServer_RequestReceived; _OSName = osName; _OSVersion = osVersion; _Log.LogInfo("Publisher started."); _CommsServer.BeginListeningForBroadcasts(); _Log.LogInfo("Publisher started listening for broadcasts."); }
/// <summary> /// Default constructor. /// </summary> /// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/> implementation, used to send and receive SSDP network messages.</param> /// <param name="osName">Then name of the operating system running the server.</param> /// <param name="osVersion">The version of the operating system running the server.</param> protected SsdpDevicePublisherBase(ISsdpCommunicationsServer communicationsServer, string osName, string osVersion) { if (communicationsServer == null) throw new ArgumentNullException("communicationsServer"); if (osName == null) throw new ArgumentNullException("osName"); if (osName.Length == 0) throw new ArgumentException("osName cannot be an empty string.", "osName"); if (osVersion == null) throw new ArgumentNullException("osVersion"); if (osVersion.Length == 0) throw new ArgumentException("osVersion cannot be an empty string.", "osName"); _SupportPnpRootDevice = true; _Devices = new List<SsdpRootDevice>(); _ReadOnlyDevices = new ReadOnlyEnumerable<SsdpRootDevice>(_Devices); _RecentSearchRequests = new Dictionary<string, SearchRequest>(StringComparer.OrdinalIgnoreCase); _Random = new Random(); _DeviceValidator = new Upnp10DeviceValidator(); //Should probably inject this later, but for now we only support 1.0. _CommsServer = communicationsServer; _CommsServer.RequestReceived += CommsServer_RequestReceived; _OSName = osName; _OSVersion = osVersion; _CommsServer.BeginListeningForBroadcasts(); }
public void IsEmptyReadOnlyEnumerableShouldPassForReadOnlyEnumerableDotEmpty() { ReadOnlyEnumerable <int> .Empty().IsEmptyReadOnlyEnumerable().Should().BeTrue(); }
public void AddToSourceListShouldThrowForEmptySource() { Action test = () => ReadOnlyEnumerable <int> .Empty().AddToSourceList(1); test.ShouldThrow <NotSupportedException>().WithMessage("Collection is read-only."); }
public void GetOrCreateReadOnlyEnumerableShouldGetForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable<int>(); var enumerable2 = enumerable.GetOrCreateReadOnlyEnumerable("Integers"); enumerable2.As<object>().Should().NotBeSameAs(ReadOnlyEnumerable<int>.Empty()); enumerable2.As<object>().Should().BeSameAs(enumerable); }
public void ConcatToReadOnlyEnumerableShouldAddForReadOnlyEnumerableSource() { var enumerable = new ReadOnlyEnumerable<ODataProperty>(); enumerable.Count().Should().Be(0); enumerable.ConcatToReadOnlyEnumerable("Properties", new ODataProperty()); enumerable.Count().Should().Be(1); }
public void EmptyOfTShouldAlwaysReturnTheSameInstance() { ReadOnlyEnumerable <ODataAction> .Empty().As <object>().Should().BeSameAs(ReadOnlyEnumerable <ODataAction> .Empty()); ReadOnlyEnumerable <ODataAction> .Empty().As <object>().Should().NotBeSameAs(ReadOnlyEnumerable <ODataFunction> .Empty()); }
public void IsEmptyReadOnlyEnumerableShouldPassForReadOnlyEnumerableDotEmpty() { var result = ReadOnlyEnumerable <int> .Empty().IsEmptyReadOnlyEnumerable(); Assert.True(result); }
public void EmptyOfTShouldAlwaysReturnTheSameInstance() { Assert.Same(ReadOnlyEnumerable <ODataAction> .Empty(), ReadOnlyEnumerable <ODataAction> .Empty()); Assert.NotSame(ReadOnlyEnumerable <ODataAction> .Empty(), ReadOnlyEnumerable <ODataFunction> .Empty()); }
/// <summary> /// Reads the content of a properties in an element (complex value, m:properties, ...) /// </summary> /// <param name="structuredType">The type which should declare the properties to be read. Optional.</param> /// <param name="properties">The list of properties to add properties to.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The element to read properties from. /// Post-Condition: XmlNodeType.Element - The element to read properties from if it is an empty element. /// XmlNodeType.EndElement - The end element of the element to read properties from. /// </remarks> private void ReadPropertiesImplementation(IEdmStructuredType structuredType, ReadOnlyEnumerable<ODataProperty> properties, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(properties != null, "properties != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertXmlCondition(XmlNodeType.Element); // Empty values are valid - they have no properties if (!this.XmlReader.IsEmptyElement) { // Read over the complex value element to its first child node (or end-element) this.XmlReader.ReadStartElement(); // WCF DS will skip over the rest of the complex value or entity properties if the first element in it is preceded with // a text node. Once the first element is found (no matter its namespace) then the non-element nodes // are ignored but skipped and the reading continues. // For ODataLib we should not do this and probably just ignore the text node even at the beginning (to be consistent). do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { // Found a property IEdmProperty edmProperty = null; bool isOpen = false; bool ignoreProperty = false; if (structuredType != null) { // Lookup the property in metadata edmProperty = ReaderValidationUtils.ValidateValuePropertyDefined(this.XmlReader.LocalName, structuredType, this.MessageReaderSettings, out ignoreProperty); if (edmProperty != null && edmProperty.PropertyKind == EdmPropertyKind.Navigation) { throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_NavigationPropertyInProperties(edmProperty.Name, structuredType)); } // If the property was not declared, it must be open. isOpen = edmProperty == null; } if (ignoreProperty) { this.XmlReader.Skip(); } else { // EdmLib bridge marks all key properties as non-nullable, but Astoria allows them to be nullable. // If the property has an annotation to ignore null values, we need to omit the property in requests. ODataNullValueBehaviorKind nullValueReadBehaviorKind = this.ReadingResponse || edmProperty == null ? ODataNullValueBehaviorKind.Default : this.Model.NullValueReadBehaviorKind(edmProperty); ODataProperty property = this.ReadProperty( false, edmProperty == null ? null : edmProperty.Name, edmProperty == null ? null : edmProperty.Type, nullValueReadBehaviorKind); Debug.Assert( property != null || nullValueReadBehaviorKind == ODataNullValueBehaviorKind.IgnoreValue, "If we don't ignore null values the property must not be null."); if (property != null) { if (isOpen) { ValidationUtils.ValidateOpenPropertyValue(property.Name, property.Value); } duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); properties.AddToSourceList(property); } } } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the complex value. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } } while (this.XmlReader.NodeType != XmlNodeType.EndElement); } }
/// <summary> /// Read a complex value from the reader. /// </summary> /// <param name="complexTypeReference">The type reference of the value to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the complex value (possibly null).</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.EndElement - the element has been read. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataComplexValue ReadComplexValue( IEdmComplexTypeReference complexTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); this.IncreaseRecursionDepth(); ODataComplexValue complexValue = new ODataComplexValue(); IEdmComplexType complexType = complexTypeReference == null ? null : (IEdmComplexType)complexTypeReference.Definition; // If we have a metadata type for the complex value, use that type name // otherwise use the type name from the payload (if there was any). complexValue.TypeName = complexType == null ? payloadTypeName : complexType.ODataFullName(); if (serializationTypeNameAnnotation != null) { complexValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } else { duplicatePropertyNamesChecker.Clear(); } ReadOnlyEnumerable<ODataProperty> properties = new ReadOnlyEnumerable<ODataProperty>(); this.ReadPropertiesImplementation(complexType, properties, duplicatePropertyNamesChecker); complexValue.Properties = properties; this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(complexValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return complexValue; }
/// <summary> /// Reads the content of a properties in an element (complex value, m:properties, ...) /// </summary> /// <param name="structuredType">The type which should declare the properties to be read. Optional.</param> /// <param name="properties">The list of properties to add properties to.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The element to read properties from. /// Post-Condition: XmlNodeType.Element - The element to read properties from if it is an empty element. /// XmlNodeType.EndElement - The end element of the element to read properties from. /// </remarks> protected void ReadProperties(IEdmStructuredType structuredType, ReadOnlyEnumerable<ODataProperty> properties, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.AssertRecursionDepthIsZero(); this.ReadPropertiesImplementation( structuredType, properties, duplicatePropertyNamesChecker); this.AssertRecursionDepthIsZero(); }
/// <summary> /// Reads the content of a properties in an element (complex value, m:properties, ...) /// </summary> /// <param name="structuredType">The type which should declare the properties to be read. Optional.</param> /// <param name="properties">The list of properties to add properties to.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The element to read properties from. /// Post-Condition: XmlNodeType.Element - The element to read properties from if it is an empty element. /// XmlNodeType.EndElement - The end element of the element to read properties from. /// </remarks> private void ReadPropertiesImplementation(IEdmStructuredType structuredType, ReadOnlyEnumerable <ODataProperty> properties, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(properties != null, "properties != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertXmlCondition(XmlNodeType.Element); // Empty values are valid - they have no properties if (!this.XmlReader.IsEmptyElement) { // Read over the complex value element to its first child node (or end-element) this.XmlReader.ReadStartElement(); // WCF DS will skip over the rest of the complex value or entity properties if the first element in it is preceded with // a text node. Once the first element is found (no matter its namespace) then the non-element nodes // are ignored but skipped and the reading continues. // For ODataLib we should not do this and probably just ignore the text node even at the beginning (to be consistent). do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { // Found a property IEdmProperty edmProperty = null; bool isOpen = false; bool ignoreProperty = false; if (structuredType != null) { // Lookup the property in metadata edmProperty = ReaderValidationUtils.ValidateValuePropertyDefined(this.XmlReader.LocalName, structuredType, this.MessageReaderSettings, out ignoreProperty); if (edmProperty != null && edmProperty.PropertyKind == EdmPropertyKind.Navigation) { throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_NavigationPropertyInProperties(edmProperty.Name, structuredType)); } // If the property was not declared, it must be open. isOpen = edmProperty == null; } if (ignoreProperty) { this.XmlReader.Skip(); } else { // EdmLib bridge marks all key properties as non-nullable, but Astoria allows them to be nullable. // If the property has an annotation to ignore null values, we need to omit the property in requests. ODataNullValueBehaviorKind nullValueReadBehaviorKind = this.ReadingResponse || edmProperty == null ? ODataNullValueBehaviorKind.Default : this.Model.NullValueReadBehaviorKind(edmProperty); ODataProperty property = this.ReadProperty( false, edmProperty == null ? null : edmProperty.Name, edmProperty == null ? null : edmProperty.Type, nullValueReadBehaviorKind); Debug.Assert( property != null || nullValueReadBehaviorKind == ODataNullValueBehaviorKind.IgnoreValue, "If we don't ignore null values the property must not be null."); if (property != null) { if (isOpen) { ValidationUtils.ValidateOpenPropertyValue(property.Name, property.Value); } duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); properties.AddToSourceList(property); } } } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the complex value. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } }while (this.XmlReader.NodeType != XmlNodeType.EndElement); } }
public void ToReadOnlyEnumerableShouldNotThrowForReadOnlyEnumerableSource() { var result = new ReadOnlyEnumerable <int>().ToReadOnlyEnumerable("Integers"); Assert.Empty(result); }
private void SetEpmValueForSegment( EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, ReadOnlyEnumerable <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."); // TODO: Server seems to have a bug where if there's an EPM for an open property where the EPM uses complex types (the source path is deeper than 1) // then it will actually materialize the property as entry level string property, with the value of the deep property value. If there are multiple deep // EPM for the same top-level open property it seems to set the entry level property multiple times with the values as they come from payload. // Client on the other hand doesn't have open properties, and always has a type, so no problem there. if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1) { throw new ODataException(ODataErrorStrings.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(ODataErrorStrings.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, propertyName); 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, existingComplexValue.Properties.ToReadOnlyEnumerable("Properties"), propertyValue); break; case EdmTypeKind.Collection: Debug.Assert(propertyType.IsNonEntityCollectionType(), "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(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind)); } }
/// <summary> /// Reads the content of a properties in an element (complex value, m:properties, ...) /// </summary> /// <param name="structuredType">The type which should declare the properties to be read. Optional.</param> /// <param name="properties">The list of properties to add properties to.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param> /// <param name="epmPresent">Whether any EPM mappings exist.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The element to read properties from. /// Post-Condition: XmlNodeType.Element - The element to read properties from if it is an empty element. /// XmlNodeType.EndElement - The end element of the element to read properties from. /// </remarks> private void ReadPropertiesImplementation(IEdmStructuredType structuredType, ReadOnlyEnumerable <ODataProperty> properties, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool epmPresent) { Debug.Assert(properties != null, "properties != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertXmlCondition(XmlNodeType.Element); // Empty values are valid - they have no properties if (!this.XmlReader.IsEmptyElement) { // Read over the complex value element to its first child node (or end-element) this.XmlReader.ReadStartElement(); do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { // Found a property IEdmProperty edmProperty = null; bool isOpen = false; bool ignoreProperty = false; if (structuredType != null) { // Lookup the property in metadata edmProperty = ReaderValidationUtils.ValidateValuePropertyDefined(this.XmlReader.LocalName, structuredType, this.MessageReaderSettings, out ignoreProperty); if (edmProperty != null && edmProperty.PropertyKind == EdmPropertyKind.Navigation) { throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_NavigationPropertyInProperties(edmProperty.Name, structuredType)); } // If the property was not declared, it must be open. isOpen = edmProperty == null; } if (ignoreProperty) { this.XmlReader.Skip(); } else { ODataNullValueBehaviorKind nullValueReadBehaviorKind = this.ReadingResponse || edmProperty == null ? ODataNullValueBehaviorKind.Default : this.Model.NullValueReadBehaviorKind(edmProperty); ODataProperty property = this.ReadProperty( edmProperty == null ? null : edmProperty.Name, edmProperty == null ? null : edmProperty.Type, nullValueReadBehaviorKind, epmPresent); Debug.Assert( property != null || nullValueReadBehaviorKind == ODataNullValueBehaviorKind.IgnoreValue, "If we don't ignore null values the property must not be null."); if (property != null) { if (isOpen) { ValidationUtils.ValidateOpenPropertyValue(property.Name, property.Value, this.MessageReaderSettings.UndeclaredPropertyBehaviorKinds); } duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); properties.AddToSourceList(property); } } } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the complex value. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } }while (this.XmlReader.NodeType != XmlNodeType.EndElement); } }