Esempio n. 1
0
        /// <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));
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Add the projection node with the given property name, property instance and target resource type, if required.
        /// </summary>
        /// <param name="propertyName">Name of the property that needs to be projected.</param>
        /// <param name="property">ResourceProperty instance containing information about the property - this will be null for open properties.</param>
        /// <param name="targetResourceType">ResourceType instance on which the property needs to be expanded.</param>
        /// <returns>A new or an existing instance of ProjectionNode.</returns>
        internal ExpandedProjectionNode AddProjectionNode(string propertyName, ResourceProperty property, ResourceType targetResourceType)
        {
            // find out if there exists another node with the given property name.
            ProjectionNode node = this.FindNode(propertyName);

            if (node == null || !ExpandedProjectionNode.ApplyPropertyToExistingNode(node, property, targetResourceType))
            {
                // just create a simple projection node for it
                node = new ProjectionNode(propertyName, property, targetResourceType);
                this.AddNode(node);
            }

            return(node as ExpandedProjectionNode);
        }
Esempio n. 3
0
        /// <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);
        }
Esempio n. 4
0
        /// <summary>Adds a new child node to this node.</summary>
        /// <param name="node">The child node to add.</param>
        private void AddNode(ProjectionNode node)
        {
            Debug.Assert(node != null, "node != null");
            Debug.Assert(
                this.nodes.Count(
                    n => n.PropertyName == node.PropertyName &&
                    (n.TargetResourceType.IsAssignableFrom(node.TargetResourceType) || node.TargetResourceType.IsAssignableFrom(n.TargetResourceType))) == 0,
                "make sure there is no node with the same property name in the inheritance hierarchy");

            this.nodes.Add(node);

            ExpandedProjectionNode expandedNode = node as ExpandedProjectionNode;

            if (expandedNode != null)
            {
                this.hasExpandedPropertyOnDerivedType = this.hasExpandedPropertyOnDerivedType || (!ResourceType.CompareReferences(this.ResourceType, node.TargetResourceType));
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Sort the list of existing projection nodes in the metadata order, first based on the type followed by the property order.
        /// </summary>
        /// <param name="existingNodes">existing projection nodes.</param>
        /// <param name="resourceTypesInMetadataOrder">list of resource types in metadata order.</param>
        /// <returns>the projection nodes in sorted manner.</returns>
        private static List <ProjectionNode> SortNodes(List <ProjectionNode> existingNodes, List <ResourceType> resourceTypesInMetadataOrder)
        {
            List <ProjectionNode> sortedNodes = new List <ProjectionNode>(existingNodes.Count);

            foreach (ResourceType resourceType in resourceTypesInMetadataOrder)
            {
                foreach (ResourceProperty property in resourceType.Properties)
                {
                    // Since the same instance of resource property can be shared by 2 different resource types,
                    // just comparing the resource property instances is not good enough. We also need to compare
                    // the type references along with the property references.
                    Debug.Assert(
                        existingNodes.Where(node => node.Property == property && node.TargetResourceType == resourceType).Count() <= 1,
                        "Can't have more than one projection segment for a given property.");

                    ProjectionNode projectionNode = existingNodes.FirstOrDefault(
                        node => node.Property == property && node.TargetResourceType == resourceType);
                    if (projectionNode != null)
                    {
                        sortedNodes.Add(projectionNode);

                        // We need to remove the node from the list, otherwise when we are going through more derived types,
                        // we might encounter the same property again, since we are walking through all the properties on
                        // a given resource type.
                        existingNodes.Remove(projectionNode);
                    }
                }
            }

            // And then append any open properties sorted alphabetically
            // We sort these since we don't want client to be able to influence
            //   the server behavior unless aboslutely necessary.
            List <ProjectionNode> openPropertyProjectionNodes =
                existingNodes.Where(node => node.Property == null).ToList();

            openPropertyProjectionNodes.Sort(new Comparison <ProjectionNode>((x, y) =>
            {
                return(String.Compare(x.PropertyName, y.PropertyName, StringComparison.Ordinal));
            }));
            sortedNodes.AddRange(openPropertyProjectionNodes);
            return(sortedNodes);
        }
        /// <summary>Adds a new child node to this node.</summary>
        /// <param name="node">The child node to add.</param>
        private void AddNode(ProjectionNode node)
        {
            Debug.Assert(node != null, "node != null");
            Debug.Assert(
                this.nodes.Count(
                    n => n.PropertyName == node.PropertyName &&
                    (n.TargetResourceType.IsAssignableFrom(node.TargetResourceType) || node.TargetResourceType.IsAssignableFrom(n.TargetResourceType))) == 0,
                "make sure there is no node with the same property name in the inheritance hierarchy");

            this.nodes.Add(node);

            ExpandedProjectionNode expandedNode = node as ExpandedProjectionNode;
            if (expandedNode != null)
            {
                this.hasExpandedPropertyOnDerivedType = this.hasExpandedPropertyOnDerivedType || (!ResourceType.CompareReferences(this.ResourceType, node.TargetResourceType));
            }
        }
        /// <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;
        }
        /// <summary>
        /// Add the projection node with the given property name, property instance and target resource type, if required.
        /// </summary>
        /// <param name="propertyName">Name of the property that needs to be projected.</param>
        /// <param name="property">ResourceProperty instance containing information about the property - this will be null for open properties.</param>
        /// <param name="targetResourceType">ResourceType instance on which the property needs to be expanded.</param>
        /// <returns>A new or an existing instance of ProjectionNode.</returns>
        internal ExpandedProjectionNode AddProjectionNode(string propertyName, ResourceProperty property, ResourceType targetResourceType)
        {
            // find out if there exists another node with the given property name.
            ProjectionNode node = this.FindNode(propertyName);
            if (node == null || !ExpandedProjectionNode.ApplyPropertyToExistingNode(node, property, targetResourceType))
            {
                // just create a simple projection node for it
                node = new ProjectionNode(propertyName, property, targetResourceType);
                this.AddNode(node);
            }

            return node as ExpandedProjectionNode;
        }