/// <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);
        }
Beispiel #2
0
        /// <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));
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #6
0
        /// <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));
        }