/// <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 = new BindingState(configuration, odataPathInfo.Segments.ToList()); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(odataPathInfo.TargetEdmType.ToTypeReference(), odataPathInfo.TargetNavigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); if (applyClause != null) { state.AggregatedPropertyNames = applyClause.GetLastAggregatedPropertyNames(); } MetadataBinder binder = new MetadataBinder(state); FilterBinder filterBinder = new FilterBinder(binder.Bind, state); FilterClause boundNode = filterBinder.BindFilter(filterToken); return(boundNode); }
/// <summary> /// Binds an Count segment token. /// </summary> /// <param name="countSegmentToken">The Count segment token to bind.</param> /// <param name="state">State of the metadata binding.</param> /// <returns>The bound Count segment token.</returns> internal QueryNode BindCountSegment(CountSegmentToken countSegmentToken) { ExceptionUtils.CheckArgumentNotNull(countSegmentToken, "countSegmentToken"); QueryNode source = this.bindMethod(countSegmentToken.NextToken); CollectionNode node = source as CollectionNode; if (node == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_CountSegmentNextTokenNotCollectionValue()); } FilterClause filterClause = null; SearchClause searchClause = null; BindingState innerBindingState = new BindingState(state.Configuration); innerBindingState.ImplicitRangeVariable = NodeFactory.CreateParameterNode(ExpressionConstants.It, node); MetadataBinder binder = new MetadataBinder(innerBindingState); if (countSegmentToken.FilterOption != null) { FilterBinder filterBinder = new FilterBinder(binder.Bind, innerBindingState); filterClause = filterBinder.BindFilter(countSegmentToken.FilterOption); } if (countSegmentToken.SearchOption != null) { SearchBinder searchBinder = new SearchBinder(binder.Bind); searchClause = searchBinder.BindSearch(countSegmentToken.SearchOption); } return(new CountNode(node, filterClause, searchClause)); }
/// <summary> /// Binds the token to a SingleValueFunctionCallNode or a SingleResourceFunctionCallNode for complex /// </summary> /// <param name="functionCallToken">Token to bind</param> /// <returns>The resulting SingleValueFunctionCallNode/SingleResourceFunctionCallNode</returns> internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken) { ExceptionUtils.CheckArgumentNotNull(functionCallToken, "functionCallToken"); ExceptionUtils.CheckArgumentNotNull(functionCallToken.Name, "functionCallToken.Name"); // Bind the parent, if present. // TODO: parent can be a collection as well, so we need to loosen this to QueryNode. QueryNode parent = null; if (state.ImplicitRangeVariable != null) { if (functionCallToken.Source != null) { parent = this.bindMethod(functionCallToken.Source); } else { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); } } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. var colNode = parent as CollectionNavigationNode; if (colNode != null && functionCallToken.Name == UriQueryConstants.CountSegment) { FilterClause filterOption = null; var filter = functionCallToken.Arguments.FirstOrDefault(a => a.ParameterName == "$filter"); if (filter != null) { MetadataBinder binder = this.BuildNewMetadataBinder(colNode.NavigationSource); FilterBinder filterBinder = new FilterBinder(binder.Bind, binder.BindingState); filterOption = filterBinder.BindFilter(filter.ValueToken); } // create a collection count node for collection node property. return(new CountNode(colNode, filterOption)); } // First see if there is a custom function for this QueryNode boundFunction; if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, parent, state, out boundFunction)) { return(boundFunction); } // then check if there is a global custom function(i.e with out a parent node) if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, null, state, out boundFunction)) { return(boundFunction); } // If there isn't, bind as Uri function // Bind all arguments List <QueryNode> argumentNodes = new List <QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar))); return(BindAsUriFunction(functionCallToken, argumentNodes)); }
/// <summary> /// Bind the filter clause <see cref="FilterClause"/> at this level. /// </summary> /// <param name="filterToken">The filter token to visit.</param> /// <returns>The null or the built filter clause.</returns> private FilterClause BindFilter(QueryToken filterToken, IEdmNavigationSource navigationSource, IEdmTypeReference elementType, HashSet <EndPathToken> generatedProperties, bool collapsed = false) { if (filterToken != null) { MetadataBinder binder = BuildNewMetadataBinder(this.Configuration, navigationSource, elementType, generatedProperties, collapsed); FilterBinder filterBinder = new FilterBinder(binder.Bind, binder.BindingState); return(filterBinder.BindFilter(filterToken)); } return(null); }
/// <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> /// Generate an expand item (and a select item for the implicit nav prop if necessary) based on an ExpandTermToken /// </summary> /// <param name="tokenIn">the expandTerm token to visit</param> /// <returns>the expand item for this expand term token.</returns> private SelectItem GenerateExpandItem(ExpandTermToken tokenIn) { ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn"); PathSegmentToken currentToken = tokenIn.PathToNavigationProp; IEdmStructuredType currentLevelEntityType = this.EdmType; List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); PathSegmentToken firstNonTypeToken = currentToken; if (currentToken.IsNamespaceOrContainerQualified()) { pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelEntityType, out firstNonTypeToken)); } IEdmProperty edmProperty = this.configuration.Resolver.ResolveProperty(currentLevelEntityType, firstNonTypeToken.Identifier); if (edmProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.FullTypeName(), currentToken.Identifier)); } IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty; IEdmStructuralProperty currentComplexProp = edmProperty as IEdmStructuralProperty; if (currentNavProp == null && currentComplexProp == null) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationPropertyOrComplexProperty(currentToken.Identifier, currentLevelEntityType.FullTypeName())); } if (currentComplexProp != null) { currentNavProp = ParseComplexTypesBeforeNavigation(currentComplexProp, ref firstNonTypeToken, pathSoFar); } // ensure that we're always dealing with proper V4 syntax if (firstNonTypeToken.NextToken != null && firstNonTypeToken.NextToken.NextToken != null) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath); } bool isRef = false; if (firstNonTypeToken.NextToken != null) { // lastly... make sure that, since we're on a NavProp, that the next token isn't null. if (firstNonTypeToken.NextToken.Identifier == UriQueryConstants.RefSegment) { isRef = true; } else { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath); } } // Add the segments in select and expand to parsed segments this.parsedSegments.AddRange(pathSoFar); IEdmNavigationSource targetNavigationSource = null; if (this.NavigationSource != null) { IEdmPathExpression bindingPath; targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp, BindingPathHelper.MatchBindingPath, this.parsedSegments, out bindingPath); } NavigationPropertySegment navSegment = new NavigationPropertySegment(currentNavProp, targetNavigationSource); pathSoFar.Add(navSegment); this.parsedSegments.Add(navSegment); // Add the navigation property segment to parsed segments for future usage. ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar); // call MetadataBinder to build the filter clause FilterClause filterOption = null; if (tokenIn.FilterOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetNavigationSource); FilterBinder filterBinder = new FilterBinder(binder.Bind, binder.BindingState); filterOption = filterBinder.BindFilter(tokenIn.FilterOption); } // call MetadataBinder again to build the orderby clause OrderByClause orderbyOption = null; if (tokenIn.OrderByOptions != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetNavigationSource); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); orderbyOption = orderByBinder.BindOrderBy(binder.BindingState, tokenIn.OrderByOptions); } SearchClause searchOption = null; if (tokenIn.SearchOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetNavigationSource); SearchBinder searchBinder = new SearchBinder(binder.Bind); searchOption = searchBinder.BindSearch(tokenIn.SearchOption); } if (isRef) { return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption)); } SelectExpandClause subSelectExpand; if (tokenIn.ExpandOption != null) { subSelectExpand = this.GenerateSubExpand(tokenIn); } else { subSelectExpand = BuildDefaultSubExpand(); } subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption); LevelsClause levelsOption = this.ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp); return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption)); }