/// <summary> /// Parse the compute option in the expand option text. /// </summary> /// <returns>The compute option for expand</returns> private ComputeToken ParseInnerCompute() { this.lexer.NextToken(); string computeText = this.ReadQueryOption(); UriQueryExpressionParser computeParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(computeParser.ParseCompute(computeText)); }
/// <summary> /// Parse the apply option in the expand option text. /// </summary> /// <returns>The apply option for expand</returns> private IEnumerable <QueryToken> ParseInnerApply() { this.lexer.NextToken(); string applyText = this.ReadQueryOption(); UriQueryExpressionParser applyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(applyParser.ParseApply(applyText)); }
/// <summary> /// Parse the orderby option in the select/expand option text. /// </summary> /// <returns>The orderby option for select/expand</returns> private IEnumerable <OrderByToken> ParseInnerOrderBy() { // advance to the equal sign this.lexer.NextToken(); string orderByText = this.ReadQueryOption(); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(orderbyParser.ParseOrderBy(orderByText)); }
/// <summary> /// Parse the filter option in the select/expand option text. /// </summary> /// <returns>The filter option for select/expand</returns> private QueryToken ParseInnerFilter() { // advance to the equal sign this.lexer.NextToken(); string filterText = this.ReadQueryOption(); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); return(filterParser.ParseFilter(filterText)); }
/// <summary> /// Parse the filter option in the $count segment. /// </summary> /// <returns>The filter option for the $count segment.</returns> private QueryToken ParseInnerFilter() { // advance to the equal sign this.lexer.NextToken(); string filterText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(ODataUriParserSettings.DefaultFilterLimit, this.UriQueryExpressionParser.EnableCaseInsensitiveBuiltinIdentifier); return(filterParser.ParseFilter(filterText)); }
/// <summary> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="parser">The UriQueryExpressionParser to read from.</param> /// <param name="endTokenKind">The token kind that marks the end of the parameters.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> private static bool TrySplitOperationParameters(this UriQueryExpressionParser parser, ExpressionTokenKind endTokenKind, out ICollection <FunctionParameterToken> splitParameters) { Debug.Assert(parser != null, "parser != null"); var lexer = parser.Lexer; var parameters = new List <FunctionParameterToken>(); splitParameters = parameters; ExpressionToken currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { return(true); } if (currentToken.Kind != ExpressionTokenKind.Identifier || lexer.PeekNextToken().Kind != ExpressionTokenKind.Equal) { return(false); } while (currentToken.Kind != endTokenKind) { lexer.ValidateToken(ExpressionTokenKind.Identifier); string identifier = lexer.CurrentToken.GetIdentifier(); lexer.NextToken(); lexer.ValidateToken(ExpressionTokenKind.Equal); lexer.NextToken(); // the below UriQueryExpressionParser.ParseExpression() is able to parse common expression per ABNF: // functionExprParameter = parameterName EQ ( parameterAlias / parameterValue ) // parameterValue = arrayOrObject // / commonExpr QueryToken parameterValue = parser.ParseExpression(); parameters.Add(new FunctionParameterToken(identifier, parameterValue)); // the above parser.ParseExpression() already moves to the next token, now get CurrentToken checking a comma followed by something currentToken = lexer.CurrentToken; if (currentToken.Kind == ExpressionTokenKind.Comma) { lexer.NextToken(); currentToken = lexer.CurrentToken; if (currentToken.Kind == endTokenKind) { // Trailing comma. throw new ODataException(ODataErrorStrings.ExpressionLexer_SyntaxError(lexer.Position, lexer.ExpressionText)); } } } return(true); }
/// <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 the <paramref name="compute"/> clause, binding /// the text into a metadata-bound list of compuations using the provided model. /// </summary> /// <param name="compute">String representation of the compute expression from the URI.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="ComputeClause"/> representing the metadata bound compute expression.</returns> private ComputeClause ParseComputeImplementation(string compute, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(compute, "compute"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); ComputeToken computeToken = expressionParser.ParseCompute(compute); // Bind it to metadata BindingState state = CreateBindingState(configuration, odataPathInfo); MetadataBinder binder = new MetadataBinder(state); ComputeBinder computeBinder = new ComputeBinder(binder.Bind); ComputeClause boundNode = computeBinder.BindCompute(computeToken); return(boundNode); }
/// <summary> /// Parses an <paramref name="orderBy "/> clause, binding /// the text into semantic nodes using the provided model. /// </summary> /// <param name="orderBy">String representation of the orderby expression.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>An <see cref="OrderByClause"/> representing the metadata bound orderby expression.</returns> private OrderByClause ParseOrderByImplementation(string orderBy, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(configuration.Model, "model"); ExceptionUtils.CheckArgumentNotNull(orderBy, "orderBy"); // Get the syntactic representation of the orderby expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.OrderByLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); var orderByQueryTokens = expressionParser.ParseOrderBy(orderBy); // Bind it to metadata BindingState state = CreateBindingState(configuration, odataPathInfo); MetadataBinder binder = new MetadataBinder(state); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); OrderByClause orderByClause = orderByBinder.BindOrderBy(state, orderByQueryTokens); return(orderByClause); }
/// <summary> /// Parses a <paramref name="filter"/> clause, binding /// the text into semantic nodes using the provided model. /// </summary> /// <param name="filter">String representation of the filter expression.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="FilterClause"/> representing the metadata bound filter expression.</returns> private FilterClause ParseFilterImplementation(string filter, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(odataPathInfo, "odataPathInfo"); ExceptionUtils.CheckArgumentNotNull(filter, "filter"); // Get the syntactic representation of the filter expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); QueryToken filterToken = expressionParser.ParseFilter(filter); // Bind it to metadata BindingState state = CreateBindingState(configuration, odataPathInfo); MetadataBinder binder = new MetadataBinder(state); FilterBinder filterBinder = new FilterBinder(binder.Bind, state); FilterClause boundNode = filterBinder.BindFilter(filterToken); return(boundNode); }
/// <summary> /// Parses the <paramref name="compute"/> clause, binding /// the text into a metadata-bound list of compuations using the provided model. /// </summary> /// <param name="compute">String representation of the compute expression from the URI.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="ComputeClause"/> representing the metadata bound compute expression.</returns> private static ComputeClause ParseComputeImplementation(string compute, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(compute, "compute"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); ComputeToken computeToken = expressionParser.ParseCompute(compute); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(odataPathInfo.TargetEdmType.ToTypeReference(), odataPathInfo.TargetNavigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); ComputeBinder computeBinder = new ComputeBinder(binder.Bind); ComputeClause boundNode = computeBinder.BindCompute(computeToken); return(boundNode); }
/// <summary> /// Parses an <paramref name="apply"/> clause, binding /// the text into a metadata-bound or dynamic properties to be applied using the provided model. /// </summary> /// <param name="apply">String representation of the apply expression.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="ApplyClause"/> representing the metadata bound apply expression.</returns> private static ApplyClause ParseApplyImplementation(string apply, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(apply, "apply"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); var applyTokens = expressionParser.ParseApply(apply); // Bind it to metadata BindingState state = new BindingState(configuration, odataPathInfo.Segments.ToList()); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(odataPathInfo.TargetEdmType.ToTypeReference(), odataPathInfo.TargetNavigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); ApplyBinder applyBinder = new ApplyBinder(binder.Bind, state, configuration, odataPathInfo); ApplyClause boundNode = applyBinder.BindApply(applyTokens); return(boundNode); }
/// <summary> /// Parse expression into syntaxs token tree, and bind it into semantics node tree. /// </summary> /// <param name="bindingState">The BindingState.</param> /// <param name="aliasValueExpression">The alias value's expression text.</param> /// <param name="parameterType">The edm type of the parameter.</param> /// <returns>The semantics node of the expression text.</returns> private SingleValueNode ParseAndBindParameterAliasValueExpression(BindingState bindingState, string aliasValueExpression, IEdmTypeReference parameterType) { // Get the syntactic representation of the filter expression // TODO: change Settings.FilterLimit to ParameterAliasValueLimit UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(bindingState.Configuration.Settings.FilterLimit); QueryToken aliasValueToken = expressionParser.ParseExpressionText(aliasValueExpression); // Special logic to handle parameter alias token. aliasValueToken = ParseComplexOrCollectionAlias(aliasValueToken, parameterType, bindingState.Model); // Get the semantic node, and check for SingleValueNode QueryNode aliasValueNode = this.bindMethod(aliasValueToken); SingleValueNode result = aliasValueNode as SingleValueNode; if (result == null) { // TODO: add string resource throw new ODataException(Strings.MetadataBinder_ParameterAliasValueExpressionNotSingleValue); } return(result); }
/// <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); }
/// <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> /// Tries to parse a collection of function parameters. Allows path and filter to share the core algorithm while representing parameters differently. /// </summary> /// <param name="parser">The UriQueryExpressionParser to read from.</param> /// <param name="splitParameters">The parameters if they were successfully split.</param> /// <returns>Whether the parameters could be split.</returns> internal static bool TrySplitFunctionParameters(this UriQueryExpressionParser parser, out ICollection <FunctionParameterToken> splitParameters) { return(parser.TrySplitOperationParameters(ExpressionTokenKind.CloseParen, out splitParameters)); }
/// <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 */) { }
/// <summary> /// Parses the <paramref name="queryUri"/> and returns a new instance of <see cref="SyntacticTree"/> /// describing the query specified by the uri. /// </summary> /// <param name="queryUri">The absolute URI which holds the query to parse. This must be a path relative to the <paramref name="serviceBaseUri"/>.</param> /// <param name="serviceBaseUri">The base URI of the service.</param> /// <param name="maxDepth">The maximum depth of any single query part. Security setting to guard against DoS attacks causing stack overflows and such.</param> /// <returns>A new instance of <see cref="SyntacticTree"/> which represents the query specified in the <paramref name="queryUri"/>.</returns> public static SyntacticTree ParseUri(Uri queryUri, Uri serviceBaseUri, int maxDepth) { ExceptionUtils.CheckArgumentNotNull(queryUri, "queryUri"); if (!queryUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(queryUri), "queryUri"); } ExceptionUtils.CheckArgumentNotNull(serviceBaseUri, "serviceBaseUri"); if (!serviceBaseUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(serviceBaseUri), "serviceBaseUri"); } if (maxDepth <= 0) { throw new ArgumentException(Strings.SyntacticTree_MaxDepthInvalid, "maxDepth"); } UriPathParser pathParser = new UriPathParser(new ODataUriParserSettings() { PathLimit = maxDepth }); var path = pathParser.ParsePathIntoSegments(queryUri, serviceBaseUri); // COMPAT 32: Differencies in query options parsing in WCF DS // // We allow non-system $ query options in the lexical space. // We allow multiple instances of a custom or non-system $ query option in the lexical space. // TODO: we need to decide whether we want to allow multiple system $ query options with the same name (OIPI suggests that this is valid); we currently don't. List <CustomQueryOptionToken> queryOptions = QueryOptionUtils.ParseQueryOptions(queryUri); IDictionary <string, string> parameterAliases = queryOptions.GetParameterAliases(); QueryToken filter = null; string filterQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FilterQueryOption); if (filterQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); filter = expressionParser.ParseFilter(filterQuery); } IEnumerable <OrderByToken> orderByTokens = null; string orderByQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.OrderByQueryOption); if (orderByQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); orderByTokens = expressionParser.ParseOrderBy(orderByQuery); } SelectToken select = null; string selectQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SelectQueryOption); if (selectQuery != null) { SelectExpandParser selectParser = new SelectExpandParser(selectQuery, ODataUriParserSettings.DefaultSelectExpandLimit); select = selectParser.ParseSelect(); } ExpandToken expand = null; string expandQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.ExpandQueryOption); if (expandQuery != null) { SelectExpandParser expandParser = new SelectExpandParser(expandQuery, ODataUriParserSettings.DefaultSelectExpandLimit); expand = expandParser.ParseExpand(); } int? skip = null; string skipQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SkipQueryOption); if (skipQuery != null) { int skipValue; if (!TryUriStringToNonNegativeInteger(skipQuery, out skipValue)) { throw new ODataException(Strings.SyntacticTree_InvalidSkipQueryOptionValue(skipQuery)); } skip = skipValue; } int? top = null; string topQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.TopQueryOption); if (topQuery != null) { int topValue; if (!TryUriStringToNonNegativeInteger(topQuery, out topValue)) { throw new ODataException(Strings.SyntacticTree_InvalidTopQueryOptionValue(topQuery)); } top = topValue; } string countQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.CountQueryOption); bool? count = ParseQueryCount(countQuery); string format = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FormatQueryOption); return(new SyntacticTree( parameterAliases, path, filter, orderByTokens, select, expand, skip, top, count, format, queryOptions.Count == 0 ? null : new ReadOnlyCollection <CustomQueryOptionToken>(queryOptions))); }