Example #1
0
        /// <summary>
        /// Parses a literal.
        /// Precondition: lexer is at a literal token type: Boolean, DateTime, Decimal, Null, String, Int64, Integer, Double, Single, Guid, Binary.
        /// </summary>
        /// <param name="expressionLexer">The expression lexer.</param>
        /// <returns>The literal query token or null if something else was found.</returns>
        private static object TryParseLiteral(this ExpressionLexer expressionLexer)
        {
            Debug.Assert(expressionLexer.CurrentToken.Kind.IsLiteralType(), "TryParseLiteral called when not at a literal type token");

            switch (expressionLexer.CurrentToken.Kind)
            {
            case ExpressionTokenKind.NullLiteral:
                return(ParseNullLiteral(expressionLexer));

            case ExpressionTokenKind.BooleanLiteral:
            case ExpressionTokenKind.DecimalLiteral:
            case ExpressionTokenKind.StringLiteral:
            case ExpressionTokenKind.Int64Literal:
            case ExpressionTokenKind.IntegerLiteral:
            case ExpressionTokenKind.DoubleLiteral:
            case ExpressionTokenKind.SingleLiteral:
            case ExpressionTokenKind.GuidLiteral:
            case ExpressionTokenKind.BinaryLiteral:
            case ExpressionTokenKind.DateLiteral:
            case ExpressionTokenKind.DateTimeOffsetLiteral:
            case ExpressionTokenKind.DurationLiteral:
            case ExpressionTokenKind.GeographyLiteral:
            case ExpressionTokenKind.GeometryLiteral:
            case ExpressionTokenKind.QuotedLiteral:
            case ExpressionTokenKind.TimeOfDayLiteral:
            case ExpressionTokenKind.CustomTypeLiteral:
                return(ParseTypedLiteral(expressionLexer, expressionLexer.CurrentToken.GetLiteralEdmTypeReference()));

            default:
                return(null);
            }
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="maxDepth">The maximum depth of each part of the query - a recursion limit.</param>
 /// <param name="lexer">The ExpressionLexer containing text to be parsed.</param>
 internal UriQueryExpressionParser(int maxDepth, ExpressionLexer lexer)
 {
     Debug.Assert(maxDepth >= 0, "maxDepth >= 0");
     Debug.Assert(lexer != null, "lexer != null");
     this.maxDepth = maxDepth;
     this.lexer    = lexer;
 }
        // parses $compute query option.
        internal ComputeToken ParseCompute(string compute)
        {
            Debug.Assert(compute != null, "compute != null");

            List <ComputeExpressionToken> transformationTokens = new List <ComputeExpressionToken>();

            if (string.IsNullOrEmpty(compute))
            {
                return(new ComputeToken(transformationTokens));
            }

            this.recursionDepth = 0;
            this.lexer          = CreateLexerForFilterOrOrderByOrApplyExpression(compute);

            while (true)
            {
                ComputeExpressionToken computed = this.ParseComputeExpression();
                transformationTokens.Add(computed);
                if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Comma)
                {
                    break;
                }

                this.lexer.NextToken();
            }

            this.lexer.ValidateToken(ExpressionTokenKind.End);

            return(new ComputeToken(transformationTokens));
        }
Example #4
0
        /// <summary>
        /// Parses typed literals.
        /// </summary>
        /// <param name="expressionLexer">The expression lexer.</param>
        /// <param name="targetTypeReference">Expected type to be parsed.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static object ParseTypedLiteral(this ExpressionLexer expressionLexer, IEdmTypeReference targetTypeReference)
        {
            UriLiteralParsingException typeParsingException;
            object targetValue = DefaultUriLiteralParser.Instance.ParseUriStringToType(expressionLexer.CurrentToken.Text, targetTypeReference, out typeParsingException);

            if (targetValue == null)
            {
                string message;

                if (typeParsingException == null)
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteral(
                        targetTypeReference.FullName(),
                        expressionLexer.CurrentToken.Text,
                        expressionLexer.CurrentToken.Position,
                        expressionLexer.ExpressionText);

                    throw new ODataException(message);
                }
                else
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteralWithReason(
                        targetTypeReference.FullName(),
                        expressionLexer.CurrentToken.Text,
                        expressionLexer.CurrentToken.Position,
                        expressionLexer.ExpressionText,
                        typeParsingException.Message);

                    throw new ODataException(message, typeParsingException);
                }
            }

            expressionLexer.NextToken();
            return(targetValue);
        }
Example #5
0
 /// <summary>
 /// Create a new CountSegmentParser.
 /// </summary>
 /// <param name="lexer">Lexer positioned at a $count identifier.</param>
 /// <param name="parser">The UriQueryExpressionParser.</param>
 public CountSegmentParser(ExpressionLexer lexer, UriQueryExpressionParser parser)
 {
     ExceptionUtils.CheckArgumentNotNull(lexer, "lexer");
     ExceptionUtils.CheckArgumentNotNull(parser, "parser");
     this.lexer  = lexer;
     this.parser = parser;
 }
        /// <summary>
        /// Parses parameter alias into token.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <returns>The parameter alias token.</returns>
        private static FunctionParameterAliasToken ParseParameterAlias(ExpressionLexer lexer)
        {
            Debug.Assert(lexer != null, "lexer != null");
            FunctionParameterAliasToken ret = new FunctionParameterAliasToken(lexer.CurrentToken.Text);

            lexer.NextToken();
            return(ret);
        }
Example #7
0
 /// <summary>
 /// Create a new FunctionCallParser.
 /// </summary>
 /// <param name="lexer">Lexer positioned at a function identifier.</param>
 /// <param name="parser">The UriQueryExpressionParser.</param>
 /// <param name="restoreStateIfFail">If set to true, catches any ODataException thrown while trying to parse function arguments.</param>
 public FunctionCallParser(ExpressionLexer lexer, UriQueryExpressionParser parser, bool restoreStateIfFail)
 {
     ExceptionUtils.CheckArgumentNotNull(lexer, "lexer");
     ExceptionUtils.CheckArgumentNotNull(parser, "parser");
     this.lexer              = lexer;
     this.parser             = parser;
     this.restoreStateIfFail = restoreStateIfFail;
 }
Example #8
0
 /// <summary>
 /// Parse an Identifier into the right QueryToken
 /// </summary>
 /// <param name="parameters">parameters passed in to the UriQueryExpressionParser</param>
 /// <param name="functionCallParser">Object to use to handle parsing function calls.</param>
 public IdentifierTokenizer(HashSet <string> parameters, IFunctionCallParser functionCallParser)
 {
     ExceptionUtils.CheckArgumentNotNull(parameters, "parameters");
     ExceptionUtils.CheckArgumentNotNull(functionCallParser, "functionCallParser");
     this.lexer              = functionCallParser.Lexer;
     this.parameters         = parameters;
     this.functionCallParser = functionCallParser;
 }
Example #9
0
        /// <summary>
        /// Parses null literals.
        /// </summary>
        /// <param name="expressionLexer">The expression lexer.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static object ParseNullLiteral(this ExpressionLexer expressionLexer)
        {
            Debug.Assert(expressionLexer.CurrentToken.Kind == ExpressionTokenKind.NullLiteral, "this.lexer.CurrentToken.InternalKind == ExpressionTokenKind.NullLiteral");

            expressionLexer.NextToken();
            ODataNullValue nullValue = new ODataNullValue();

            return(nullValue);
        }
        /// <summary>
        /// Parses null literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseNullLiteral(ExpressionLexer lexer)
        {
            Debug.Assert(lexer != null, "lexer != null");
            Debug.Assert(lexer.CurrentToken.Kind == ExpressionTokenKind.NullLiteral, "this.lexer.CurrentToken.InternalKind == ExpressionTokenKind.NullLiteral");

            LiteralToken result = new LiteralToken(null, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
Example #11
0
        /// <summary>Reads the next token, checks that it is a literal token type, converts to to a Common Language Runtime value as appropriate, and returns the value.</summary>
        /// <param name="expressionLexer">The expression lexer.</param>
        /// <returns>The value represented by the next token.</returns>
        internal static object ReadLiteralToken(this ExpressionLexer expressionLexer)
        {
            expressionLexer.NextToken();

            if (expressionLexer.CurrentToken.Kind.IsLiteralType())
            {
                return(TryParseLiteral(expressionLexer));
            }

            throw new ODataException(ODataErrorStrings.ExpressionLexer_ExpectedLiteralToken(expressionLexer.CurrentToken.Text));
        }
Example #12
0
        /// <summary>
        /// Parse expression text into Token.
        /// </summary>
        /// <param name="expressionText">The expression string to Parse.</param>
        /// <returns>The lexical token representing the expression text.</returns>
        internal QueryToken ParseSearch(string expressionText)
        {
            Debug.Assert(expressionText != null, "expressionText != null");

            this.recursionDepth = 0;
            this.lexer          = new SearchLexer(expressionText);
            QueryToken result = this.ParseExpression();

            this.lexer.ValidateToken(ExpressionTokenKind.End);

            return(result);
        }
        /// <summary>
        /// Parse expression text into Token.
        /// </summary>
        /// <param name="expressionText">The expression string to Parse.</param>
        /// <returns>The lexical token representing the expression text.</returns>
        internal QueryToken ParseExpressionText(string expressionText)
        {
            Debug.Assert(expressionText != null, "expressionText != null");

            this.recursionDepth = 0;
            this.lexer          = CreateLexerForFilterOrOrderByOrApplyExpression(expressionText);
            QueryToken result = this.ParseExpression();

            this.lexer.ValidateToken(ExpressionTokenKind.End);

            return(result);
        }
        internal IEnumerable <QueryToken> ParseApply(string apply)
        {
            Debug.Assert(apply != null, "apply != null");

            List <QueryToken> transformationTokens = new List <QueryToken>();

            if (string.IsNullOrEmpty(apply))
            {
                return(transformationTokens);
            }

            this.recursionDepth = 0;
            this.lexer          = CreateLexerForFilterOrOrderByOrApplyExpression(apply);

            while (true)
            {
                switch (this.lexer.CurrentToken.GetIdentifier())
                {
                case ExpressionConstants.KeywordAggregate:
                    transformationTokens.Add(ParseAggregate());
                    break;

                case ExpressionConstants.KeywordFilter:
                    transformationTokens.Add(ParseApplyFilter());
                    break;

                case ExpressionConstants.KeywordGroupBy:
                    transformationTokens.Add(ParseGroupBy());
                    break;

                case ExpressionConstants.KeywordCompute:
                    transformationTokens.Add(ParseCompute());
                    break;

                default:
                    throw ParseError(ODataErrorStrings.UriQueryExpressionParser_KeywordOrIdentifierExpected(supportedKeywords, this.lexer.CurrentToken.Position, this.lexer.ExpressionText));
                }

                // '/' indicates there are more transformations
                if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Slash)
                {
                    break;
                }

                this.lexer.NextToken();
            }

            this.lexer.ValidateToken(ExpressionTokenKind.End);

            return(new ReadOnlyCollection <QueryToken>(transformationTokens));
        }
        /// <summary>
        /// Tries to parse a collection of function parameters for path.
        /// </summary>
        /// <param name="parenthesisExpression">The contents of the parentheses portion of the current path segment.</param>
        /// <param name="configuration">The ODataUriParserConfiguration to create a UriQueryExpressionParser.</param>
        /// <param name="splitParameters">The parameters if they were successfully split.</param>
        /// <returns>Whether the parameters could be split.</returns>
        internal static bool TrySplitOperationParameters(string parenthesisExpression, ODataUriParserConfiguration configuration, out ICollection <FunctionParameterToken> splitParameters)
        {
            ExpressionLexer          lexer  = new ExpressionLexer(parenthesisExpression, true /*moveToFirstToken*/, false /*useSemicolonDelimeter*/, true /*parsingFunctionParameters*/);
            UriQueryExpressionParser parser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, lexer);
            var ret = parser.TrySplitOperationParameters(ExpressionTokenKind.End, out splitParameters);

            // check duplicate names
            if (splitParameters.Select(t => t.ParameterName).Distinct().Count() != splitParameters.Count)
            {
                throw new ODataException(ODataErrorStrings.FunctionCallParser_DuplicateParameterOrEntityKeyName);
            }

            return(ret);
        }
        /// <summary>
        /// Parses a literal.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <returns>The literal query token or null if something else was found.</returns>
        internal static LiteralToken TryParseLiteral(ExpressionLexer lexer)
        {
            Debug.Assert(lexer != null, "lexer != null");

            switch (lexer.CurrentToken.Kind)
            {
            case ExpressionTokenKind.BooleanLiteral:
            case ExpressionTokenKind.DateLiteral:
            case ExpressionTokenKind.DecimalLiteral:
            case ExpressionTokenKind.StringLiteral:
            case ExpressionTokenKind.Int64Literal:
            case ExpressionTokenKind.IntegerLiteral:
            case ExpressionTokenKind.DoubleLiteral:
            case ExpressionTokenKind.SingleLiteral:
            case ExpressionTokenKind.GuidLiteral:
            case ExpressionTokenKind.BinaryLiteral:
            case ExpressionTokenKind.GeographyLiteral:
            case ExpressionTokenKind.GeometryLiteral:
            case ExpressionTokenKind.QuotedLiteral:
            case ExpressionTokenKind.DurationLiteral:
            case ExpressionTokenKind.TimeOfDayLiteral:
            case ExpressionTokenKind.DateTimeOffsetLiteral:
            case ExpressionTokenKind.CustomTypeLiteral:
                IEdmTypeReference literalEdmTypeReference = lexer.CurrentToken.GetLiteralEdmTypeReference();

                // Why not using EdmTypeReference.FullName? (literalEdmTypeReference.FullName)
                string edmConstantName = GetEdmConstantNames(literalEdmTypeReference);
                return(ParseTypedLiteral(lexer, literalEdmTypeReference, edmConstantName));

            case ExpressionTokenKind.BracedExpression:
            case ExpressionTokenKind.BracketedExpression:
            case ExpressionTokenKind.ParenthesesExpression:
            {
                LiteralToken result = new LiteralToken(lexer.CurrentToken.Text, lexer.CurrentToken.Text);
                lexer.NextToken();
                return(result);
            }

            case ExpressionTokenKind.NullLiteral:
                return(ParseNullLiteral(lexer));

            default:
                return(null);
            }
        }
Example #17
0
        /// <summary>
        /// Build the SelectOption strategy.
        /// TODO: Really should not take the clauseToParse here. Instead it should be provided with a call to ParseSelect() or ParseExpand().
        /// </summary>
        /// <param name="clauseToParse">the clause to parse</param>
        /// <param name="maxRecursiveDepth">max recursive depth</param>
        /// <param name="enableCaseInsensitiveBuiltinIdentifier">Whether to allow case insensitive for builtin identifier.</param>
        /// <param name="enableNoDollarQueryOptions">Whether to enable no dollar query options.</param>
        public SelectExpandParser(
            string clauseToParse,
            int maxRecursiveDepth,
            bool enableCaseInsensitiveBuiltinIdentifier = false,
            bool enableNoDollarQueryOptions             = false)
        {
            this.maxRecursiveDepth = maxRecursiveDepth;

            // Set max recursive depth for path, $filter, $orderby and $search to maxRecursiveDepth in case they were not be be specified.
            this.MaxPathDepth    = maxRecursiveDepth;
            this.MaxFilterDepth  = maxRecursiveDepth;
            this.MaxOrderByDepth = maxRecursiveDepth;
            this.MaxSearchDepth  = maxRecursiveDepth;

            // Sets up our lexer. We don't turn useSemicolonDelimiter on since the parsing code for expand options,
            // which is the only thing that needs it, is in a different class that uses it's own lexer.
            this.lexer = clauseToParse != null ? new ExpressionLexer(clauseToParse, false /*moveToFirstToken*/, false /*useSemicolonDelimiter*/) : null;

            this.enableCaseInsensitiveBuiltinIdentifier = enableCaseInsensitiveBuiltinIdentifier;

            this.enableNoDollarQueryOptions = enableNoDollarQueryOptions;
        }
        /// <summary>
        /// Parses typed literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <param name="targetTypeReference">Expected type to be parsed.</param>
        /// <param name="targetTypeName">The EDM type name of the expected type to be parsed.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseTypedLiteral(ExpressionLexer lexer, IEdmTypeReference targetTypeReference, string targetTypeName)
        {
            Debug.Assert(lexer != null, "lexer != null");

            UriLiteralParsingException typeParsingException;
            object targetValue = DefaultUriLiteralParser.Instance.ParseUriStringToType(lexer.CurrentToken.Text, targetTypeReference, out typeParsingException);

            if (targetValue == null)
            {
                string message;

                if (typeParsingException == null)
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteral(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText);

                    throw ParseError(message);
                }
                else
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteralWithReason(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText,
                        typeParsingException.Message);

                    throw ParseError(message, typeParsingException);
                }
            }

            LiteralToken result = new LiteralToken(targetValue, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
        /// <summary>
        /// Parse the complex/collection value in parameter alias.
        /// </summary>
        /// <param name="queryToken">The parsed token.</param>
        /// <param name="parameterType">The expected parameter type.</param>
        /// <param name="model">The model</param>
        /// <returns>Token with complex/collection value passed.</returns>
        private static QueryToken ParseComplexOrCollectionAlias(QueryToken queryToken, IEdmTypeReference parameterType, IEdmModel model)
        {
            LiteralToken valueToken = queryToken as LiteralToken;
            string       valueStr;

            if (valueToken != null && (valueStr = valueToken.Value as string) != null && !string.IsNullOrEmpty(valueToken.OriginalText))
            {
                var lexer = new ExpressionLexer(valueToken.OriginalText, true /*moveToFirstToken*/, false /*useSemicolonDelimiter*/, true /*parsingFunctionParameters*/);
                if (lexer.CurrentToken.Kind == ExpressionTokenKind.BracketedExpression || lexer.CurrentToken.Kind == ExpressionTokenKind.BracedExpression)
                {
                    object result = valueStr;
                    if (!parameterType.IsStructured() && !parameterType.IsStructuredCollectionType())
                    {
                        result = ODataUriUtils.ConvertFromUriLiteral(valueStr, ODataVersion.V4, model, parameterType);
                    }

                    // For non-primitive type, we have to pass parameterType to LiteralToken, then to ConstantNode so the service can know what the type it is.
                    return(new LiteralToken(result, valueToken.OriginalText, parameterType));
                }
            }

            return(queryToken);
        }
Example #20
0
        /// <summary>
        /// Read a query option from the lexer.
        /// </summary>
        /// <returns>The query option as a string.</returns>
        internal static string ReadQueryOption(ExpressionLexer lexer)
        {
            if (lexer.CurrentToken.Kind != ExpressionTokenKind.Equal)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(lexer.ExpressionText));
            }

            // get the full text from the current location onward
            // there could be literals like 'A string literal; tricky!' in there, so we need to be careful.
            // Also there could be more nested (...) expressions that we ignore until we recurse enough times to get there.
            string expressionText = lexer.AdvanceThroughExpandOption();

            if (lexer.CurrentToken.Kind == ExpressionTokenKind.SemiColon)
            {
                // Move over the ';' separator
                lexer.NextToken();
                return(expressionText);
            }

            // If there wasn't a semicolon, it MUST be the last option. We must be at ')' in this case
            lexer.ValidateToken(ExpressionTokenKind.CloseParen);
            return(expressionText);
        }
        /// <summary>
        /// Parses the $orderby expression.
        /// </summary>
        /// <param name="orderBy">The $orderby expression string to parse.</param>
        /// <returns>The enumeraion of lexical tokens representing order by tokens.</returns>
        internal IEnumerable <OrderByToken> ParseOrderBy(string orderBy)
        {
            Debug.Assert(orderBy != null, "orderBy != null");

            this.recursionDepth = 0;
            this.lexer          = CreateLexerForFilterOrOrderByOrApplyExpression(orderBy);

            List <OrderByToken> orderByTokens = new List <OrderByToken>();

            while (true)
            {
                QueryToken expression = this.ParseExpression();
                bool       ascending  = true;
                if (this.TokenIdentifierIs(ExpressionConstants.KeywordAscending))
                {
                    this.lexer.NextToken();
                }
                else if (this.TokenIdentifierIs(ExpressionConstants.KeywordDescending))
                {
                    this.lexer.NextToken();
                    ascending = false;
                }

                OrderByToken orderByToken = new OrderByToken(expression, ascending ? OrderByDirection.Ascending : OrderByDirection.Descending);
                orderByTokens.Add(orderByToken);
                if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Comma)
                {
                    break;
                }

                this.lexer.NextToken();
            }

            this.lexer.ValidateToken(ExpressionTokenKind.End);

            return(new ReadOnlyCollection <OrderByToken>(orderByTokens));
        }
Example #22
0
        /// <summary>
        /// This is temp work around for $filter $orderby parameter expression which contains complex or collection
        ///     like "Fully.Qualified.Namespace.CanMoveToAddresses(addresses=[{\"Street\":\"NE 24th St.\",\"City\":\"Redmond\"},{\"Street\":\"Pine St.\",\"City\":\"Seattle\"}])";
        /// TODO:  $filter $orderby parameter expression which contains nested complex or collection should NOT be supported in this way
        ///     but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p));
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="operation">IEdmFunction or IEdmOperation</param>
        /// <param name="parameterTokens">The tokens to bind.</param>
        /// <param name="enableCaseInsensitive">Whether to enable case-insensitive when resolving parameter name.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param>
        /// <returns>The FunctionParameterTokens with complex or collection values converted from string like "{...}", or "[..,..,..]".</returns>
        private static ICollection <FunctionParameterToken> HandleComplexOrCollectionParameterValueIfExists(IEdmModel model, IEdmOperation operation, ICollection <FunctionParameterToken> parameterTokens, bool enableCaseInsensitive, bool enableUriTemplateParsing = false)
        {
            ICollection <FunctionParameterToken> partiallyParsedParametersWithComplexOrCollection = new Collection <FunctionParameterToken>();

            foreach (FunctionParameterToken paraToken in parameterTokens)
            {
                FunctionParameterToken funcParaToken;
                IEdmOperationParameter functionParameter = operation.FindParameter(paraToken.ParameterName);
                if (enableCaseInsensitive && functionParameter == null)
                {
                    functionParameter = ODataUriResolver.ResolveOperationParameterNameCaseInsensitive(operation, paraToken.ParameterName);

                    // The functionParameter can not be null here, else this method won't be called.
                    funcParaToken = new FunctionParameterToken(functionParameter.Name, paraToken.ValueToken);
                }
                else
                {
                    funcParaToken = paraToken;
                }

                FunctionParameterAliasToken aliasToken = funcParaToken.ValueToken as FunctionParameterAliasToken;
                if (aliasToken != null)
                {
                    aliasToken.ExpectedParameterType = functionParameter.Type;
                }

                LiteralToken valueToken = funcParaToken.ValueToken as LiteralToken;
                string       valueStr   = null;
                if (valueToken != null && (valueStr = valueToken.Value as string) != null && !string.IsNullOrEmpty(valueToken.OriginalText))
                {
                    ExpressionLexer lexer = new ExpressionLexer(valueToken.OriginalText, true /*moveToFirstToken*/, false /*useSemicolonDelimiter*/, true /*parsingFunctionParameters*/);
                    if (lexer.CurrentToken.Kind == ExpressionTokenKind.BracketedExpression || lexer.CurrentToken.Kind == ExpressionTokenKind.BracedExpression)
                    {
                        object result;
                        UriTemplateExpression expression;

                        if (enableUriTemplateParsing && UriTemplateParser.TryParseLiteral(lexer.CurrentToken.Text, functionParameter.Type, out expression))
                        {
                            result = expression;
                        }
                        else if (!functionParameter.Type.IsStructured() && !functionParameter.Type.IsStructuredCollectionType())
                        {
                            // ExpressionTokenKind.BracketedExpression means text like [1,2]
                            // so now try convert it to collection type value:
                            result = ODataUriUtils.ConvertFromUriLiteral(valueStr, ODataVersion.V4, model, functionParameter.Type);
                        }
                        else
                        {
                            // For complex & colleciton of complex directly return the raw string.
                            partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken);
                            continue;
                        }

                        LiteralToken           newValueToken    = new LiteralToken(result, valueToken.OriginalText);
                        FunctionParameterToken newFuncParaToken = new FunctionParameterToken(funcParaToken.ParameterName, newValueToken);
                        partiallyParsedParametersWithComplexOrCollection.Add(newFuncParaToken);
                        continue;
                    }
                }

                partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken);
            }

            return(partiallyParsedParametersWithComplexOrCollection);
        }
Example #23
0
        /// <summary>
        /// Building of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc)
        /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term.
        /// </summary>
        /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param>
        /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param>
        /// <returns>The list of expand term tokens based on the path token, and all available expand options.</returns>
        internal List <ExpandTermToken> BuildExpandTermToken(PathSegmentToken pathToken, string optionsText)
        {
            // Setup a new lexer for parsing the optionsText
            this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/);

            // $expand option with star only support $ref option, $expand option property could be "*" or "*/$ref", special logic will be adopted.
            if (pathToken.Identifier == UriQueryConstants.Star || (pathToken.Identifier == UriQueryConstants.RefSegment && pathToken.NextToken.Identifier == UriQueryConstants.Star))
            {
                return(BuildStarExpandTermToken(pathToken));
            }

            QueryToken filterOption = null;
            IEnumerable <OrderByToken> orderByOptions = null;
            long?                    topOption        = null;
            long?                    skipOption       = null;
            bool?                    countOption      = null;
            long?                    levelsOption     = null;
            QueryToken               searchOption     = null;
            SelectToken              selectOption     = null;
            ExpandToken              expandOption     = null;
            ComputeToken             computeOption    = null;
            IEnumerable <QueryToken> applyOptions     = null;

            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen)
            {
                // advance past the '('
                this.lexer.NextToken();

                // Check for (), which is not allowed.
                if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen)
                {
                    throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier));
                }

                // Look for all the supported query options
                while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
                {
                    string text = this.enableCaseInsensitiveBuiltinIdentifier
                        ? this.lexer.CurrentToken.Text.ToLowerInvariant()
                        : this.lexer.CurrentToken.Text;

                    // Prepend '$' prefix if needed.
                    if (this.enableNoDollarQueryOptions && !text.StartsWith(UriQueryConstants.DollarSign, StringComparison.Ordinal))
                    {
                        text = string.Format(CultureInfo.InvariantCulture, "{0}{1}", UriQueryConstants.DollarSign, text);
                    }

                    switch (text)
                    {
                    case ExpressionConstants.QueryOptionFilter:     // inner $filter
                        filterOption = ParseInnerFilter();
                        break;

                    case ExpressionConstants.QueryOptionOrderby:     // inner $orderby
                        orderByOptions = ParseInnerOrderBy();
                        break;

                    case ExpressionConstants.QueryOptionTop:     // inner $top
                        topOption = ParseInnerTop();
                        break;

                    case ExpressionConstants.QueryOptionSkip:     // innner $skip
                        skipOption = ParseInnerSkip();
                        break;

                    case ExpressionConstants.QueryOptionCount:     // inner $count
                        countOption = ParseInnerCount();
                        break;

                    case ExpressionConstants.QueryOptionSearch:     // inner $search
                        searchOption = ParseInnerSearch();
                        break;

                    case ExpressionConstants.QueryOptionLevels:     // inner $level
                        levelsOption = ParseInnerLevel();
                        break;

                    case ExpressionConstants.QueryOptionSelect:     // inner $select
                        selectOption = ParseInnerSelect(pathToken);
                        break;

                    case ExpressionConstants.QueryOptionExpand:     // inner $expand
                        expandOption = ParseInnerExpand(pathToken);
                        break;

                    case ExpressionConstants.QueryOptionCompute:     // inner $compute
                        computeOption = ParseInnerCompute();
                        break;

                    case ExpressionConstants.QueryOptionApply:     // inner $apply
                        applyOptions = ParseInnerApply();
                        break;

                    default:
                    {
                        throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
                    }
                    }
                }

                // Move past the ')'
                this.lexer.NextToken();
            }

            // Either there was no '(' at all or we just read past the ')' so we should be at the end
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
            }

            // TODO, there should be some check here in case pathToken identifier is $ref, select, expand and levels options are not allowed.
            List <ExpandTermToken> expandTermTokenList = new List <ExpandTermToken>();
            ExpandTermToken        currentToken        = new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption,
                                                                             skipOption, countOption, levelsOption, searchOption, selectOption, expandOption, computeOption, applyOptions);

            expandTermTokenList.Add(currentToken);

            return(expandTermTokenList);
        }
Example #24
0
        /// <summary>
        /// Building off a PathSegmentToken, continue parsing any select options (nested $filter, $expand, etc)
        /// to build up an SelectTermToken which fully represents the tree that makes up this select term.
        /// </summary>
        /// <param name="pathToken">The PathSegmentToken representing the parsed select path whose options we are now parsing.</param>
        /// <param name="optionsText">A string of the text between the parenthesis after a select option.</param>
        /// <returns>The select term token based on the path token, and all available select options.</returns>
        internal SelectTermToken BuildSelectTermToken(PathSegmentToken pathToken, string optionsText)
        {
            // Setup a new lexer for parsing the optionsText
            this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/);

            QueryToken filterOption = null;
            IEnumerable <OrderByToken> orderByOptions = null;
            long?        topOption     = null;
            long?        skipOption    = null;
            bool?        countOption   = null;
            QueryToken   searchOption  = null;
            SelectToken  selectOption  = null;
            ComputeToken computeOption = null;

            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen)
            {
                // advance past the '('
                this.lexer.NextToken();

                // Check for (), which is not allowed.
                if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen)
                {
                    throw new ODataException(ODataErrorStrings.UriParser_MissingSelectOption(pathToken.Identifier));
                }

                // Look for all the supported query options
                while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
                {
                    string text = this.enableCaseInsensitiveBuiltinIdentifier
                        ? this.lexer.CurrentToken.Text.ToLowerInvariant()
                        : this.lexer.CurrentToken.Text;

                    // Prepend '$' prefix if needed.
                    if (this.enableNoDollarQueryOptions && !text.StartsWith(UriQueryConstants.DollarSign, StringComparison.Ordinal))
                    {
                        text = string.Format(CultureInfo.InvariantCulture, "{0}{1}", UriQueryConstants.DollarSign, text);
                    }

                    switch (text)
                    {
                    case ExpressionConstants.QueryOptionFilter:     // inner $filter
                        filterOption = ParseInnerFilter();
                        break;

                    case ExpressionConstants.QueryOptionOrderby:     // inner $orderby
                        orderByOptions = ParseInnerOrderBy();
                        break;

                    case ExpressionConstants.QueryOptionTop:     // inner $top
                        topOption = ParseInnerTop();
                        break;

                    case ExpressionConstants.QueryOptionSkip:     // innner $skip
                        skipOption = ParseInnerSkip();
                        break;

                    case ExpressionConstants.QueryOptionCount:     // inner $count
                        countOption = ParseInnerCount();
                        break;

                    case ExpressionConstants.QueryOptionSearch:     // inner $search
                        searchOption = ParseInnerSearch();
                        break;

                    case ExpressionConstants.QueryOptionSelect:     // inner $select
                        selectOption = ParseInnerSelect(pathToken);
                        break;

                    case ExpressionConstants.QueryOptionCompute:     // inner $compute
                        computeOption = ParseInnerCompute();
                        break;

                    default:
                        throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
                    }
                }

                // Move past the ')'
                this.lexer.NextToken();
            }

            // Either there was no '(' at all or we just read past the ')' so we should be at the end
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
            }

            return(new SelectTermToken(pathToken, filterOption, orderByOptions, topOption, skipOption, countOption, searchOption, selectOption, computeOption));
        }
 /// <summary>
 /// Constructs a term parser.
 /// </summary>
 /// <param name="lexer">Lexer to use for parsing the term. Should be position at the term to parse.</param>
 /// <param name="maxPathLength">Max length of a select or expand path.</param>
 /// <param name="isSelect">True if we are parsing select, false if we are parsing expand.</param>
 internal SelectExpandTermParser(ExpressionLexer lexer, int maxPathLength, bool isSelect)
 {
     this.lexer         = lexer;
     this.maxPathLength = maxPathLength;
     this.isSelect      = isSelect;
 }
Example #26
0
 /// <summary>
 /// Create a new FunctionCallParser.
 /// </summary>
 /// <param name="lexer">Lexer positioned at a function identifier.</param>
 /// <param name="parser">The UriQueryExpressionParser.</param>
 public FunctionCallParser(ExpressionLexer lexer, UriQueryExpressionParser parser)
     : this(lexer, parser, false /* restoreStateIfFail */)
 {
 }
Example #27
0
        /// <summary>Attempts to parse key values from the specified text.</summary>
        /// <param name='text'>Text to parse (not null).</param>
        /// <param name='instance'>After invocation, the parsed key instance.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param>
        /// <returns>
        /// true if the key instance was parsed; false if there was a
        /// syntactic error.
        /// </returns>
        /// <remarks>
        /// The returned instance contains only string values. To get typed values, a call to
        /// TryConvertValues is necessary.
        /// </remarks>
        private static bool TryParseFromUri(string text, out SegmentArgumentParser instance, bool enableUriTemplateParsing)
        {
            Debug.Assert(text != null, "text != null");

            Dictionary <string, string> namedValues = null;
            List <string> positionalValues          = null;

            // parse keys just like function parameters
            ExpressionLexer          lexer      = new ExpressionLexer(string.Concat("(", text, ")"), true, false);
            UriQueryExpressionParser exprParser = new UriQueryExpressionParser(ODataUriParserSettings.DefaultFilterLimit /* default limit for parsing key value */, lexer);
            var tmp = (new FunctionCallParser(lexer, exprParser)).ParseArgumentListOrEntityKeyList();

            if (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                instance = null;
                return(false);
            }

            if (tmp.Length == 0)
            {
                instance = Empty;
                return(true);
            }

            foreach (FunctionParameterToken t in tmp)
            {
                string       valueText    = null;
                LiteralToken literalToken = t.ValueToken as LiteralToken;
                if (literalToken != null)
                {
                    valueText = literalToken.OriginalText;

                    // disallow "{...}" if enableUriTemplateParsing is false (which could have been seen as valid function parameter, e.g. array notation)
                    if (!enableUriTemplateParsing && UriTemplateParser.IsValidTemplateLiteral(valueText))
                    {
                        instance = null;
                        return(false);
                    }
                }
                else
                {
                    DottedIdentifierToken dottedIdentifierToken = t.ValueToken as DottedIdentifierToken; // for enum
                    if (dottedIdentifierToken != null)
                    {
                        valueText = dottedIdentifierToken.Identifier;
                    }
                }

                if (valueText != null)
                {
                    if (t.ParameterName == null)
                    {
                        if (namedValues != null)
                        {
                            instance = null; // We cannot mix named and non-named values.
                            return(false);
                        }

                        CreateIfNull(ref positionalValues);
                        positionalValues.Add(valueText);
                    }
                    else
                    {
                        if (positionalValues != null)
                        {
                            instance = null; // We cannot mix named and non-named values.
                            return(false);
                        }

                        CreateIfNull(ref namedValues);
                        namedValues.Add(t.ParameterName, valueText);
                    }
                }
                else
                {
                    instance = null;
                    return(false);
                }
            }

            instance = new SegmentArgumentParser(namedValues, positionalValues, false, enableUriTemplateParsing);
            return(true);
        }
Example #28
0
        /// <summary>
        /// Building off of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc)
        /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term.
        /// </summary>
        /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param>
        /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param>
        /// <returns>The list of expand term tokens based on the path token, and all available expand options.</returns>
        internal List <ExpandTermToken> BuildExpandTermToken(PathSegmentToken pathToken, string optionsText)
        {
            // Setup a new lexer for parsing the optionsText
            this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/);

            // $expand option with star only support $ref option, $expand option property could be "*" or "*/$ref", special logic will be adopted.
            if (pathToken.Identifier == UriQueryConstants.Star || (pathToken.Identifier == UriQueryConstants.RefSegment && pathToken.NextToken.Identifier == UriQueryConstants.Star))
            {
                return(BuildStarExpandTermToken(pathToken));
            }

            QueryToken filterOption = null;
            IEnumerable <OrderByToken> orderByOptions = null;
            long?       topOption    = null;
            long?       skipOption   = null;
            bool?       countOption  = null;
            long?       levelsOption = null;
            QueryToken  searchOption = null;
            SelectToken selectOption = null;
            ExpandToken expandOption = null;

            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen)
            {
                // advance past the '('
                this.lexer.NextToken();

                // Check for (), which is not allowed.
                if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen)
                {
                    throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier));
                }

                // Look for all the supported query options
                while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
                {
                    string text = this.enableCaseInsensitiveBuiltinIdentifier
                        ? this.lexer.CurrentToken.Text.ToLowerInvariant()
                        : this.lexer.CurrentToken.Text;

                    // Prepend '$' prefix if needed.
                    if (this.enableNoDollarQueryOptions && !text.StartsWith(UriQueryConstants.DollarSign, StringComparison.Ordinal))
                    {
                        text = string.Format(CultureInfo.InvariantCulture, "{0}{1}", UriQueryConstants.DollarSign, text);
                    }

                    switch (text)
                    {
                    case ExpressionConstants.QueryOptionFilter:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string filterText = this.ReadQueryOption();

                        UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier);
                        filterOption = filterParser.ParseFilter(filterText);
                        break;
                    }

                    case ExpressionConstants.QueryOptionOrderby:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string orderByText = this.ReadQueryOption();

                        UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier);
                        orderByOptions = orderbyParser.ParseOrderBy(orderByText);
                        break;
                    }

                    case ExpressionConstants.QueryOptionTop:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string topText = this.ReadQueryOption();

                        // TryParse requires a non-nullable non-negative long.
                        long top;
                        if (!long.TryParse(topText, out top) || top < 0)
                        {
                            throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidTopOption(topText));
                        }

                        topOption = top;
                        break;
                    }

                    case ExpressionConstants.QueryOptionSkip:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string skipText = this.ReadQueryOption();

                        // TryParse requires a non-nullable non-negative long.
                        long skip;
                        if (!long.TryParse(skipText, out skip) || skip < 0)
                        {
                            throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText));
                        }

                        skipOption = skip;
                        break;
                    }

                    case ExpressionConstants.QueryOptionCount:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string countText = this.ReadQueryOption();
                        switch (countText)
                        {
                        case ExpressionConstants.KeywordTrue:
                        {
                            countOption = true;
                            break;
                        }

                        case ExpressionConstants.KeywordFalse:
                        {
                            countOption = false;
                            break;
                        }

                        default:
                        {
                            throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText));
                        }
                        }

                        break;
                    }

                    case ExpressionConstants.QueryOptionLevels:
                    {
                        levelsOption = ResolveLevelOption();
                        break;
                    }

                    case ExpressionConstants.QueryOptionSearch:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string searchText = this.ReadQueryOption();

                        SearchParser searchParser = new SearchParser(this.MaxSearchDepth);
                        searchOption = searchParser.ParseSearch(searchText);

                        break;
                    }

                    case ExpressionConstants.QueryOptionSelect:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string selectText = this.ReadQueryOption();

                        SelectExpandParser innerSelectParser = new SelectExpandParser(selectText, this.maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier);
                        selectOption = innerSelectParser.ParseSelect();
                        break;
                    }

                    case ExpressionConstants.QueryOptionExpand:
                    {
                        // advance to the equal sign
                        this.lexer.NextToken();
                        string expandText = this.ReadQueryOption();

                        // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future.
                        IEdmStructuredType targetEntityType = null;
                        if (this.resolver != null && this.parentEntityType != null)
                        {
                            var parentProperty = this.resolver.ResolveProperty(parentEntityType, pathToken.Identifier) as IEdmNavigationProperty;

                            // it is a navigation property, need to find the type.
                            // Like $expand=Friends($expand=Trips($expand=*)), when expandText becomes "Trips($expand=*)",
                            // find navigation property Trips of Friends, then get Entity type of Trips.
                            if (parentProperty != null)
                            {
                                targetEntityType = parentProperty.ToEntityType();
                            }
                        }

                        SelectExpandParser innerExpandParser = new SelectExpandParser(
                            resolver,
                            expandText,
                            targetEntityType,
                            this.maxRecursionDepth - 1,
                            this.enableCaseInsensitiveBuiltinIdentifier,
                            this.enableNoDollarQueryOptions);
                        expandOption = innerExpandParser.ParseExpand();
                        break;
                    }

                    default:
                    {
                        throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
                    }
                    }
                }

                // Move past the ')'
                this.lexer.NextToken();
            }

            // Either there was no '(' at all or we just read past the ')' so we should be at the end
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
            }

            // TODO, there should be some check here in case pathToken identifier is $ref, select, expand and levels options are not allowed.
            List <ExpandTermToken> expandTermTokenList = new List <ExpandTermToken>();
            ExpandTermToken        currentToken        = new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption,
                                                                             skipOption, countOption, levelsOption, searchOption, selectOption, expandOption);

            expandTermTokenList.Add(currentToken);

            return(expandTermTokenList);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="maxDepth">The maximum depth of each part of the query - a recursion limit.</param>
 /// <param name="lexer">The ExpressionLexer containing text to be parsed.</param>
 internal UriQueryExpressionParser(int maxDepth, ExpressionLexer lexer) : this(maxDepth)
 {
     Debug.Assert(lexer != null, "lexer != null");
     this.lexer = lexer;
 }