/// <summary>
        /// Initialises a new instance of the <see cref="OrderByProperty"/> class.
        /// </summary>
        /// <param name="rawValue">The raw value.</param>
        /// <param name="model">The model.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="rawValue"/> or <paramref name="model"/> are null.</exception>
        /// <exception cref="ODataException">Thrown if there is an error parsing the <paramref name="rawValue"/>.</exception>
        internal OrderByProperty(string rawValue, EdmComplexType model)
        {
            RawValue = rawValue ?? throw new ArgumentNullException(nameof(rawValue));

            if (model is null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (rawValue.IndexOf(' ') == -1)
            {
                PropertyPath = PropertyPath.For(rawValue, model);
            }
            else
            {
                PropertyPath = PropertyPath.For(rawValue.SubstringBefore(' '), model);

                if (rawValue.EndsWith(" asc", StringComparison.Ordinal))
                {
                    Direction = OrderByDirection.Ascending;
                }
                else if (rawValue.EndsWith(" desc", StringComparison.Ordinal))
                {
                    Direction = OrderByDirection.Descending;
                }
                else
                {
                    throw ODataException.BadRequest(ExceptionMessage.InvalidOrderByDirection(rawValue.SubstringAfter(' '), PropertyPath.Property.Name), "$orderby");
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Initialises a new instance of the <see cref="SelectExpandQueryOption" /> class.
        /// </summary>
        /// <param name="rawValue">The raw request value.</param>
        /// <param name="model">The model.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="rawValue"/> is null.</exception>
        internal SelectExpandQueryOption(string rawValue, EdmComplexType model)
            : base(rawValue)
        {
            if (model is null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            var propertyPaths = new List <PropertyPath>();

            foreach (string propertyPathName in rawValue.Slice(',', rawValue.IndexOf('=') + 1))
            {
                if (propertyPathName == "*")
                {
                    if (rawValue.StartsWith("$select", StringComparison.Ordinal))
                    {
                        propertyPaths.AddRange(model.Properties.Where(p => !p.IsNavigable).Select(PropertyPath.For));
                    }
                    else if (rawValue.StartsWith("$expand", StringComparison.Ordinal))
                    {
                        propertyPaths.AddRange(model.Properties.Where(p => p.IsNavigable).Select(PropertyPath.For));
                    }
                }
                else
                {
                    propertyPaths.Add(PropertyPath.For(propertyPathName, model));
                }
            }

            PropertyPaths = propertyPaths;
        }
            public WhenConstructed()
            {
                TestHelper.EnsureEDM();

                EdmComplexType model = EntityDataModel.Current.EntitySets["Customers"].EdmType;

                _operand = new PropertyAccessNode(PropertyPath.For(model.GetProperty("CompanyName")));
                _node    = new UnaryOperatorNode(_operand, _unaryOperatorKind);
            }
            public WhenConstructed()
            {
                TestHelper.EnsureEDM();

                EdmComplexType model = EntityDataModel.Current.EntitySets["Customers"].EdmType;

                _propertyPath = PropertyPath.For(model.GetProperty("CompanyName"));
                _node         = new PropertyAccessNode(_propertyPath);
            }
            private QueryNode ParseFunctionCallNode()
            {
                BinaryOperatorNode binaryNode = null;
                FunctionCallNode   node       = null;

                var stack = new Stack <FunctionCallNode>();

                while (_tokens.Count > 0)
                {
                    Token token = _tokens.Dequeue();

                    switch (token.TokenType)
                    {
                    case TokenType.Base64Binary:
                    case TokenType.Date:
                    case TokenType.DateTimeOffset:
                    case TokenType.Decimal:
                    case TokenType.Double:
                    case TokenType.Duration:
                    case TokenType.EdmType:
                    case TokenType.Enum:
                    case TokenType.False:
                    case TokenType.Guid:
                    case TokenType.Integer:
                    case TokenType.Null:
                    case TokenType.Single:
                    case TokenType.String:
                    case TokenType.TimeOfDay:
                    case TokenType.True:
                        ConstantNode constantNode = ConstantNodeParser.ParseConstantNode(token);

                        if (stack.Count > 0)
                        {
                            stack.Peek().AddParameter(constantNode);
                        }
                        else
                        {
                            if (binaryNode == null)
                            {
                                throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter");
                            }

                            binaryNode.Right = constantNode;
                        }

                        break;

                    case TokenType.BinaryOperator:
                        binaryNode = new BinaryOperatorNode(node, token.Value.ToBinaryOperatorKind(), null);
                        break;

                    case TokenType.CloseParentheses:
                        if (_groupingDepth == 0)
                        {
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the closing parenthesis not expected", token.Position), "$filter");
                        }

                        _groupingDepth--;

                        if (stack.Count > 0)
                        {
                            FunctionCallNode lastNode = stack.Pop();

                            if (stack.Count > 0)
                            {
                                stack.Peek().AddParameter(lastNode);
                            }
                            else
                            {
                                if (binaryNode != null)
                                {
                                    binaryNode.Right = lastNode;
                                }
                                else
                                {
                                    node = lastNode;
                                }
                            }
                        }

                        break;

                    case TokenType.Comma:
                        if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses)
                        {
                            // If there is a comma in a function call, there should be another parameter followed by a closing comma
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has a missing parameter or extra comma", token.Position), "$filter");
                        }

                        break;

                    case TokenType.FunctionName:
                        node = new FunctionCallNode(token.Value);
                        break;

                    case TokenType.OpenParentheses:
                        if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses)
                        {
                            // All OData functions have at least 1 or 2 parameters
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has no parameters specified", token.Position), "$filter");
                        }

                        _groupingDepth++;
                        stack.Push(node);
                        break;

                    case TokenType.PropertyName:
                        var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model));

                        if (stack.Count > 0)
                        {
                            stack.Peek().AddParameter(propertyAccessNode);
                        }
                        else
                        {
                            if (binaryNode == null)
                            {
                                throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter");
                            }

                            binaryNode.Right = propertyAccessNode;
                        }

                        break;

                    default:
                        throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter");
                    }
                }

                if (binaryNode != null)
                {
                    return(binaryNode);
                }

                return(node);
            }
            private QueryNode ParsePropertyAccessNode()
            {
                QueryNode result = null;

                QueryNode          leftNode     = null;
                BinaryOperatorKind operatorKind = BinaryOperatorKind.None;
                QueryNode          rightNode    = null;

                while (_tokens.Count > 0)
                {
                    Token token = _tokens.Dequeue();

                    switch (token.TokenType)
                    {
                    case TokenType.Base64Binary:
                    case TokenType.Date:
                    case TokenType.DateTimeOffset:
                    case TokenType.Decimal:
                    case TokenType.Double:
                    case TokenType.Duration:
                    case TokenType.Enum:
                    case TokenType.False:
                    case TokenType.Guid:
                    case TokenType.Integer:
                    case TokenType.Null:
                    case TokenType.Single:
                    case TokenType.String:
                    case TokenType.TimeOfDay:
                    case TokenType.True:
                        rightNode = ConstantNodeParser.ParseConstantNode(token);
                        break;

                    case TokenType.BinaryOperator:
                        if (operatorKind != BinaryOperatorKind.None)
                        {
                            result    = new BinaryOperatorNode(leftNode, operatorKind, rightNode);
                            leftNode  = null;
                            rightNode = null;
                        }

                        operatorKind = token.Value.ToBinaryOperatorKind();
                        break;

                    case TokenType.CloseParentheses:
                        _groupingDepth--;
                        break;

                    case TokenType.FunctionName:
                        rightNode = new FunctionCallNode(token.Value);
                        break;

                    case TokenType.OpenParentheses:
                        _groupingDepth++;
                        break;

                    case TokenType.PropertyName:
                        var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model));

                        if (leftNode is null)
                        {
                            leftNode = propertyAccessNode;
                        }
                        else if (rightNode is null)
                        {
                            rightNode = propertyAccessNode;
                        }

                        break;

                    default:
                        throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter");
                    }
                }

                result = result is null
                    ? new BinaryOperatorNode(leftNode, operatorKind, rightNode)
                    : new BinaryOperatorNode(result, operatorKind, leftNode ?? rightNode);

                return(result);
            }