/// <summary>Creates a new instance of the <see cref="T:Microsoft.OData.Service.Providers.ResourceAssociationSetEnd" /> class.</summary>
        /// <param name="resourceSet">The resource set to which the <see cref="T:Microsoft.OData.Service.Providers.ResourceAssociationSetEnd" /> end belongs.</param>
        /// <param name="resourceType">The resource type to which the <see cref="T:Microsoft.OData.Service.Providers.ResourceAssociationSetEnd" /> end belongs.</param>
        /// <param name="resourceProperty">The resource property that returns the <see cref="T:Microsoft.OData.Service.Providers.ResourceAssociationSetEnd" /> end.</param>
        public ResourceAssociationSetEnd(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
        {
            WebUtil.CheckArgumentNull(resourceSet, "resourceSet");
            WebUtil.CheckArgumentNull(resourceType, "resourceType");

            if (resourceProperty != null && (resourceType.TryResolvePropertyName(resourceProperty.Name) == null || resourceProperty.TypeKind != ResourceTypeKind.EntityType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourcePropertyMustBeNavigationPropertyOnResourceType);
            }

            if (!resourceSet.ResourceType.IsAssignableFrom(resourceType) && !resourceType.IsAssignableFrom(resourceSet.ResourceType))
            {
                throw new ArgumentException(Strings.ResourceAssociationSetEnd_ResourceTypeMustBeAssignableToResourceSet);
            }

            this.resourceSet = resourceSet;
            this.resourceType = resourceType;

            // Note that for the TargetEnd, resourceProperty can be null.
            this.resourceProperty = resourceProperty;
        }
        /// <summary>
        /// Verify that the property referred by the existing node and the new segment are both open properties or declared properties
        /// and if the existing node is an expand node, make sure that the target resource types are the same.
        /// </summary>
        /// <param name="existingNode">Existing node with the same property name.</param>
        /// <param name="property">ResourceProperty instance for the property refered by the new segment.</param>
        /// <param name="targetResourceType">TargetResourceType for the new segment.</param>
        /// <param name="expandNode">true if the existingNode is an expand node.</param>
        private static void VerifyPropertyMismatchAndExpandSelectMismatchScenario(ProjectionNode existingNode, ResourceProperty property, ResourceType targetResourceType, bool expandNode)
        {
            Debug.Assert(
                existingNode.TargetResourceType.IsAssignableFrom(targetResourceType) ||
                targetResourceType.IsAssignableFrom(existingNode.TargetResourceType),
                "This method must be called if the existingNode and targetResourceType are in the same inheritance chain");
            Debug.Assert(!expandNode || existingNode is ExpandedProjectionNode, "If expandNode is true, then the existingNode must be an ExpandedProjectionNode");

            if (property != existingNode.Property)
            {
                // If the property are not the same - it means one of them must be null.
                // This is only possible if super type is open and the property resolves to an open property.
                Debug.Assert(property == null || existingNode.Property == null, "One of the properties must be null, since the types belong to the same inheritance chain, and cannot have different property instance from a given property name");

                // Currently we do not support scenarios where one refers to the open property on the supertype
                // and declared property on the sub type.
                throw DataServiceException.CreateBadRequestError(
                    Strings.RequestQueryProcessor_CannotSpecifyOpenPropertyAndDeclaredPropertyAtTheSameTime(
                        existingNode.PropertyName,
                        property == null ? targetResourceType.FullName : existingNode.TargetResourceType.FullName,
                        property == null ? existingNode.TargetResourceType.FullName : targetResourceType.FullName));
            }
            
            if (!ResourceType.CompareReferences(targetResourceType, existingNode.TargetResourceType) && expandNode)
            {
                // If expand and select are specified on the same property within a given type
                // hierarchy, currently we enforce that they must be specified on the same type
                throw DataServiceException.CreateBadRequestError(
                    Strings.RequestQueryProcessor_SelectAndExpandCannotBeSpecifiedTogether(existingNode.PropertyName));
            }
        }
        /// <summary>
        /// Whether the given property can be applied to the existing node.
        /// </summary>
        /// <param name="existingNode">Existing node with the same property name.</param>
        /// <param name="property">ResourceProperty instance for the property refered by the new segment.</param>
        /// <param name="targetResourceType">TargetResourceType for the new segment.</param>
        /// <returns>true if the given property can be applied to the existing node, otherwise returns false.</returns>
        /// <remarks>In case this function returns true, it might modify the TargetResourceType property of the existing node.</remarks>
        private static bool ApplyPropertyToExistingNode(ProjectionNode existingNode, ResourceProperty property, ResourceType targetResourceType)
        {
            Debug.Assert(existingNode != null, "existingNode != null");
            Debug.Assert(property == null || property.Name == existingNode.PropertyName, "property == null || property.Name == existingNode.PropertyName");

            // This is just quicker way of determining if declared property belongs to different sub-trees.
            // Since any given property can be declared once in a chain of derived types, if the property instances
            // are not the same, we can assume that they do not belong to the same hierarchy chain.
            // Even if we do not have this check, everything will just work, since we check the type assignability.
            if (property != null && existingNode.Property != null && property != existingNode.Property)
            {
                return false;
            }

            ExpandedProjectionNode expansionNode = existingNode as ExpandedProjectionNode;

            // If the target resource type of the new segment is a super type, we might 
            // need to update the target resource type of the existing node.
            // Since IsAssignableFrom returns true when they are equal, it isn't require
            // to do anything, but if we do, it should still work.
            if (targetResourceType.IsAssignableFrom(existingNode.TargetResourceType))
            {
                ExpandedProjectionNode.VerifyPropertyMismatchAndExpandSelectMismatchScenario(
                    existingNode,
                    property,
                    targetResourceType,
                    expansionNode != null);

                existingNode.TargetResourceType = targetResourceType;
                return true;
            }

            // If the existing node is already a super type, then there is nothing to do
            // other than validating the error scenarios.
            if (existingNode.TargetResourceType.IsAssignableFrom(targetResourceType))
            {
                ExpandedProjectionNode.VerifyPropertyMismatchAndExpandSelectMismatchScenario(
                    existingNode,
                    property,
                    targetResourceType,
                    expansionNode != null);
                return true;
            }

            return false;
        }
 public IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
 {
     return this.resourceTypes.Where(type => resourceType.IsAssignableFrom(type) && type != resourceType);
 }