/// <summary>
        /// Processes the specified order-by token.
        /// </summary>
        /// <param name="query">The query tree constructed so far.</param>
        /// <param name="orderByToken">The order-by token to bind.</param>
        /// <returns>Returns the combined query including the ordering.</returns>
        private QueryNode ProcessSingleOrderBy(QueryNode query, OrderByQueryToken orderByToken)
        {
            Debug.Assert(query != null, "query != null");
            ExceptionUtils.CheckArgumentNotNull(orderByToken, "orderByToken");

            CollectionQueryNode entityCollection = query.AsEntityCollectionNode();
            if (entityCollection == null)
            {
                throw new ODataException(Strings.MetadataBinder_OrderByNotApplicable);
            }

            this.parameter = new ParameterQueryNode() { ParameterType = entityCollection.ItemType };

            QueryNode expressionNode = this.Bind(orderByToken.Expression);

            // TODO: shall we really restrict order-by expressions to primitive types?
            SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode;
            if (expressionResultNode == null ||
                (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind()))
            {
                throw new ODataException(Strings.MetadataBinder_OrderByExpressionNotSingleValue);
            }

            query = new OrderByQueryNode()
            {
                Collection = entityCollection,
                Direction = orderByToken.Direction,
                Parameter = this.parameter,
                Expression = expressionResultNode
            };

            this.parameter = null;

            return query;
        }
        /// <summary>
        /// Processes the top operator (if any) and returns the combined query.
        /// </summary>
        /// <param name="query">The query tree constructed so far.</param>
        /// <param name="top">The top amount or null if none was specified.</param>
        /// <returns>
        /// The unmodified <paramref name="query"/> if no top is specified or the combined
        /// query tree including the top operator if it was specified.
        /// </returns>
        private static QueryNode ProcessTop(QueryNode query, int? top)
        {
            ExceptionUtils.CheckArgumentNotNull(query, "query");

            if (top.HasValue)
            {
                CollectionQueryNode entityCollection = query.AsEntityCollectionNode();
                if (entityCollection == null)
                {
                    throw new ODataException(Strings.MetadataBinder_TopNotApplicable);
                }

                int topValue = top.Value;
                if (topValue < 0)
                {
                    throw new ODataException(Strings.MetadataBinder_TopRequiresNonNegativeInteger(topValue.ToString(CultureInfo.CurrentCulture)));
                }

                query = new TopQueryNode()
                {
                    Collection = entityCollection,
                    Amount = new ConstantQueryNode { Value = topValue }
                };
            }

            return query;
        }
        /// <summary>
        /// Processes the filter of the query (if any).
        /// </summary>
        /// <param name="query">The query tree constructed so far.</param>
        /// <param name="filter">The filter to bind.</param>
        /// <returns>If no filter is specified, returns the <paramref name="query"/> unchanged. If a filter is specified it returns the combined query including the filter.</returns>
        private QueryNode ProcessFilter(QueryNode query, QueryToken filter)
        {
            ExceptionUtils.CheckArgumentNotNull(query, "query");

            if (filter != null)
            {
                CollectionQueryNode entityCollection = query.AsEntityCollectionNode();
                if (entityCollection == null)
                {
                    throw new ODataException(Strings.MetadataBinder_FilterNotApplicable);
                }

                this.parameter = new ParameterQueryNode() { ParameterType = entityCollection.ItemType };

                QueryNode expressionNode = this.Bind(filter);

                SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode;
                if (expressionResultNode == null ||
                    (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind()))
                {
                    throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue);
                }

                // The type may be null here if the query statically represents the null literal
                // TODO: once we support open types/properties a 'null' type will mean 'we don't know the type'. Review.
                IEdmTypeReference expressionResultType = expressionResultNode.TypeReference;
                if (expressionResultType != null)
                {
                    IEdmPrimitiveTypeReference primitiveExpressionResultType = expressionResultType.AsPrimitiveOrNull();
                    if (primitiveExpressionResultType == null || primitiveExpressionResultType.PrimitiveKind() != EdmPrimitiveTypeKind.Boolean)
                    {
                        throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue);
                    }
                }

                query = new FilterQueryNode()
                {
                    Collection = entityCollection,
                    Parameter = this.parameter,
                    Expression = expressionResultNode
                };

                this.parameter = null;
            }

            return query;
        }
        /// <summary>
        /// Processes the filter of the query (if any).
        /// </summary>
        /// <param name="query">The query tree constructed so far.</param>
        /// <param name="filter">The filter to bind.</param>
        /// <returns>If no filter is specified, returns the <paramref name="query"/> unchanged. If a filter is specified it returns the combined query including the filter.</returns>
        private QueryNode ProcessFilter(QueryNode query, QueryToken filter)
        {
            ExceptionUtils.CheckArgumentNotNull(query, "query");

            if (filter != null)
            {
                CollectionQueryNode entityCollection = query.AsEntityCollectionNode();
                if (entityCollection == null)
                {
                    throw new ODataException(Strings.MetadataBinder_FilterNotApplicable);
                }

                this.parameter = new ParameterQueryNode() { ParameterResourceType = entityCollection.ItemType };

                QueryNode expressionNode = this.Bind(filter);

                SingleValueQueryNode expressionResultNode = expressionNode as SingleValueQueryNode;
                if (expressionResultNode == null ||
                    (expressionResultNode.ResourceType != null && expressionResultNode.ResourceType.ResourceTypeKind != ResourceTypeKind.Primitive))
                {
                    throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue);
                }

                // The resource type may be null here if the query statically represents the null literal
                // TODO: once we support open types/properties a 'null' resource type will mean 'we don't know the type'. Review.
                if (expressionResultNode.ResourceType != null &&
                    !this.ResourceTypesEqual(expressionResultNode.ResourceType, PrimitiveTypeUtils.BoolResourceType) &&
                    !this.ResourceTypesEqual(expressionResultNode.ResourceType, PrimitiveTypeUtils.NullableBoolResourceType))
                {
                    throw new ODataException(Strings.MetadataBinder_FilterExpressionNotSingleValue);
                }

                query = new FilterQueryNode()
                {
                    Collection = entityCollection,
                    Parameter = this.parameter,
                    Expression = expressionResultNode
                };

                this.parameter = null;
            }

            return query;
        }