Пример #1
0
 /// <summary>
 /// Constructor for binderbase.
 /// </summary>
 /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param>
 /// <param name="state">State of the metadata binding.</param>
 protected BinderBase(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
 {
     ExceptionUtils.CheckArgumentNotNull(bindMethod, "bindMethod");
     ExceptionUtils.CheckArgumentNotNull(state, "state");
     this.bindMethod = bindMethod;
     this.state = state;
 }
Пример #2
0
        public void BindApplyWithAggregateAndFilterShouldReturnApplyClause()
        {
            var tokens = _parser.ParseApply("aggregate(StockQuantity with sum as TotalPrice)/filter(TotalPrice eq 100)");
            var metadataBiner = new MetadataBinder(_bindingState);
            var binder = new ApplyBinder(metadataBiner.Bind, _bindingState);
            var actual = binder.BindApply(tokens);

            actual.Should().NotBeNull();
            actual.Transformations.Should().HaveCount(2);

            var transformations = actual.Transformations.ToList();
            var filter = transformations[1] as FilterTransformationNode;

            filter.Should().NotBeNull();
            filter.Kind.Should().Be(TransformationNodeKind.Filter);

            var filtareClause = filter.FilterClause;
            filtareClause.Expression.Should().NotBeNull();
            var binaryOperation = filtareClause.Expression as BinaryOperatorNode;
            binaryOperation.Should().NotBeNull();
            var propertyConvertNode = binaryOperation.Left as ConvertNode;
            propertyConvertNode.Should().NotBeNull();
            var propertyAccess = propertyConvertNode.Source as SingleValueOpenPropertyAccessNode;
            propertyAccess.Should().NotBeNull();
            propertyAccess.Name.Should().Be("TotalPrice");
        }
Пример #3
0
 public void BindOrderByShouldReturnAnOrderByNodeForEachToken()
 {
     var metadataBinder = new MetadataBinder(this.bindingState);
     this.orderbyBinder = new OrderByBinder(metadataBinder.Bind);
     var tokens = GetDummyOrderbyTokenList(7);
     var result = orderbyBinder.BindOrderBy(this.bindingState, tokens);
     result.ThenBy.ThenBy.ThenBy.ThenBy.ThenBy.ThenBy.ThenBy.Should().BeNull();
 }
Пример #4
0
 public void BindOrderByShouldOrderByInEasyToConsumeOrder()
 {
     var metadataBinder = new MetadataBinder(this.bindingState);
     this.orderbyBinder = new OrderByBinder(metadataBinder.Bind);
     var tokens = GetDummyOrderbyTokenList(2); // $orderby=prop0, prop1
     var result = orderbyBinder.BindOrderBy(this.bindingState, tokens);
     result.Expression.ShouldBeConstantQueryNode("prop0");
     result.ThenBy.Expression.ShouldBeConstantQueryNode("prop1");
 }
Пример #5
0
        internal static List<OperationSegmentParameter> BindSegmentParameters(ODataUriParserConfiguration configuration, IEdmOperation functionOrOpertion, ICollection<FunctionParameterToken> segmentParameterTokens)
        {
            // TODO: HandleComplexOrCollectionParameterValueIfExists is temp work around for single copmlex or colleciton type, it can't handle nested complex or collection value.
            ICollection<FunctionParameterToken> parametersParsed = FunctionCallBinder.HandleComplexOrCollectionParameterValueIfExists(configuration.Model, functionOrOpertion, segmentParameterTokens, configuration.Resolver.EnableCaseInsensitive, configuration.EnableUriTemplateParsing);

            // Bind it to metadata
            BindingState state = new BindingState(configuration);
            state.ImplicitRangeVariable = null;
            state.RangeVariables.Clear();
            MetadataBinder binder = new MetadataBinder(state);
            List<OperationSegmentParameter> boundParameters = new List<OperationSegmentParameter>();

            IDictionary<string, SingleValueNode> input = new Dictionary<string, SingleValueNode>(StringComparer.Ordinal);
            foreach (var paraToken in parametersParsed)
            {
                // TODO: considering another better exception
                if (paraToken.ValueToken is EndPathToken)
                {
                    throw new ODataException(Strings.MetadataBinder_ParameterNotInScope(
                        string.Format(CultureInfo.InvariantCulture, "{0}={1}", paraToken.ParameterName, (paraToken.ValueToken as EndPathToken).Identifier)));
                }

                SingleValueNode boundNode = (SingleValueNode)binder.Bind(paraToken.ValueToken);

                if (!input.ContainsKey(paraToken.ParameterName))
                {
                    input.Add(paraToken.ParameterName, boundNode);
                }
            }

            IDictionary<IEdmOperationParameter, SingleValueNode> result = configuration.Resolver.ResolveOperationParameters(functionOrOpertion, input);

            foreach (var item in result)
            {
                SingleValueNode boundNode = item.Value;

                // ensure node type is compatible with parameter type.
                var sourceTypeReference = boundNode.GetEdmTypeReference();
                bool sourceIsNullOrOpenType = (sourceTypeReference == null);
                if (!sourceIsNullOrOpenType)
                {
                    // if the node has been rewritten, no further conversion is needed.
                    if (!TryRewriteIntegralConstantNode(ref boundNode, item.Key.Type))
                    {
                        boundNode = MetadataBindingUtils.ConvertToTypeIfNeeded(boundNode, item.Key.Type);
                    }
                }

                OperationSegmentParameter boundParamer = new OperationSegmentParameter(item.Key.Name, boundNode);
                boundParameters.Add(boundParamer);
            }

            return boundParameters;
        }
Пример #6
0
 /// <summary>
 /// Constructs a EnumBinder with the given method to be used binding the parent token if needed.
 /// </summary>
 /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param>
 internal EnumBinder(MetadataBinder.QueryTokenVisitor bindMethod)
 {
     this.bindMethod = bindMethod;
 }
Пример #7
0
 /// <summary>
 /// Constructs a InnerPathTokenBinder.
 /// </summary>
 /// <param name="bindMethod">Bind method to use for binding a parent node, if needed.</param>
 /// <param name="state">State of the metadata binding.</param>
 internal InnerPathTokenBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
     : base(bindMethod, state)
 {
 }
Пример #8
0
 /// <summary>
 /// Constructs a DottedIdentifierBinder with the given method to be used binding the parent token if needed.
 /// </summary>
 /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param>
 /// <param name="state">State of the metadata binding.</param>
 internal DottedIdentifierBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
     : base(bindMethod, state)
 {
 }
Пример #9
0
        /// <summary>
        /// Parses an <paramref name="orderBy "/> clause on the given <paramref name="elementType"/>, 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="elementType">Type that the orderby clause refers to.</param>
        /// <param name="navigationSource">NavigationSource that the elements are from.</param>
        /// <returns>An <see cref="OrderByClause"/> representing the metadata bound orderby expression.</returns>
        private OrderByClause ParseOrderByImplementation(string orderBy, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource)
        {
            ExceptionUtils.CheckArgumentNotNull(configuration, "configuration");
            ExceptionUtils.CheckArgumentNotNull(configuration.Model, "model");
            ExceptionUtils.CheckArgumentNotNull(elementType, "elementType");
            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 = new BindingState(configuration);
            state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource);
            state.RangeVariables.Push(state.ImplicitRangeVariable);
            if (applyClause != null)
            {
                state.AggregatedPropertyNames = applyClause.GetLastAggregatedPropertyNames();
            }

            MetadataBinder binder = new MetadataBinder(state);
            OrderByBinder orderByBinder = new OrderByBinder(binder.Bind);
            OrderByClause orderByClause = orderByBinder.BindOrderBy(state, orderByQueryTokens);

            return orderByClause;
        }
Пример #10
0
        /// <summary>
        /// Parses a <paramref name="filter"/> clause on the given <paramref name="elementType"/>, 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="elementType">Type that the filter clause refers to.</param>
        /// <param name="navigationSource">Navigation source that the elements being filtered are from.</param>
        /// <returns>A <see cref="FilterClause"/> representing the metadata bound filter expression.</returns>
        private FilterClause ParseFilterImplementation(string filter, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource)
        {
            ExceptionUtils.CheckArgumentNotNull(configuration, "configuration");
            ExceptionUtils.CheckArgumentNotNull(elementType, "elementType");
            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);
            state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource);
            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;
        }
        public void InnerPathTokenBinderShouldFailIfPropertySourceIsNotASingleValue()
        {
            var state = new BindingState(configuration);
            state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet());
            var metadataBinder = new MetadataBinder(state);
            var binder = new InnerPathTokenBinder(metadataBinder.Bind, state);
            var token = new InnerPathToken("MyDog", new InnerPathToken("MyPeople", null, null), null);

            Action bind = () => binder.BindInnerPathSegment(token);
            bind.ShouldThrow<ODataException>().WithMessage(Strings.MetadataBinder_PropertyAccessSourceNotSingleValue("MyDog"));
        }
 private void ResetState()
 {
     BindingState state = new BindingState(this.configuration);
     state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetPersonTypeReference(), HardCodedTestModel.GetPeopleSet());
     state.RangeVariables.Push(state.ImplicitRangeVariable);
     binder = new MetadataBinder(state);
     this.functionCallBinder = new FunctionCallBinder(binder.Bind, state);
 }
Пример #13
0
        public void ShouldNotThrowIfTypeNotOpenButAggregateApplied()
        {
            var token = new EndPathToken("Color", new RangeVariableToken("a"));
            EntityCollectionNode entityCollectionNode = new EntitySetNode(HardCodedTestModel.GetDogsSet());
            SingleValueNode parentNode = new EntityRangeVariableReferenceNode("a", new EntityRangeVariable("a", HardCodedTestModel.GetPersonTypeReference(), entityCollectionNode));

            var state = new BindingState(this.configuration);
            state.AggregatedPropertyNames = new List<string> { "Color" };
            var metadataBinder = new MetadataBinder(state);
            var endPathBinder = new EndPathBinder(metadataBinder.Bind, state);
            var propertyNode = endPathBinder.GeneratePropertyAccessQueryForOpenType(
                token, parentNode);

            propertyNode.ShouldBeSingleValueOpenPropertyAccessQueryNode("Color");
        }
Пример #14
0
        public void ShouldThrowIfTypeNotOpen()
        {
            var token = new EndPathToken("Color", new RangeVariableToken("a"));
            EntityCollectionNode entityCollectionNode = new EntitySetNode(HardCodedTestModel.GetDogsSet());
            SingleValueNode parentNode = new EntityRangeVariableReferenceNode("a", new EntityRangeVariable("a", HardCodedTestModel.GetPersonTypeReference(), entityCollectionNode));

            var state = new BindingState(this.configuration);
            var metadataBinder = new MetadataBinder(state);
            var endPathBinder = new EndPathBinder(metadataBinder.Bind, state);
            Action getQueryNode = () => endPathBinder.GeneratePropertyAccessQueryForOpenType(
                token, parentNode);

            getQueryNode.ShouldThrow<ODataException>().WithMessage(
                Strings.MetadataBinder_PropertyNotDeclared(parentNode.GetEdmTypeReference().FullName(),
                                                                                    token.Identifier));
        }
Пример #15
0
        public void ShouldThrowNotImplementedIfTypeIsOpen()
        {
            const string OpenPropertyName = "Style";
            var token = new EndPathToken(OpenPropertyName, new RangeVariableToken("a"));
            EntityCollectionNode entityCollectionNode = new EntitySetNode(HardCodedTestModel.GetPaintingsSet());
            SingleValueNode parentNode = new EntityRangeVariableReferenceNode("a", new EntityRangeVariable("a", HardCodedTestModel.GetPaintingTypeReference(), entityCollectionNode));
            
            var state = new BindingState(this.configuration);
            var metadataBinder = new MetadataBinder(state);
            var endPathBinder = new EndPathBinder(metadataBinder.Bind, state);
            var propertyNode = endPathBinder.GeneratePropertyAccessQueryForOpenType(
                token, parentNode);

            propertyNode.ShouldBeSingleValueOpenPropertyAccessQueryNode(OpenPropertyName);
        }
Пример #16
0
        /// <summary>
        /// Process the remaining query options (represent the set of custom query options after
        /// service operation parameters and system query options have been removed).
        /// </summary>
        /// <param name="bindingState">the current state of the binding algorithm.</param>
        /// <param name="bindMethod">pointer to a binder method.</param>
        /// <returns>The list of <see cref="QueryNode"/> instances after binding.</returns>
        public static List<QueryNode> ProcessQueryOptions(BindingState bindingState, MetadataBinder.QueryTokenVisitor bindMethod)
        {
            List<QueryNode> customQueryOptionNodes = new List<QueryNode>();
            foreach (CustomQueryOptionToken queryToken in bindingState.QueryOptions)
            {
                QueryNode customQueryOptionNode = bindMethod(queryToken);
                if (customQueryOptionNode != null)
                {
                    customQueryOptionNodes.Add(customQueryOptionNode);
                }
            }

            bindingState.QueryOptions = null;
            return customQueryOptionNodes;
        }
Пример #17
0
 /// <summary>
 /// Creates a FilterBinder.
 /// </summary>
 /// <param name="bindMethod">Method to use to visit the token tree and bind the tokens recursively.</param>
 /// <param name="state">State to use for binding.</param>
 internal FilterBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
 {
     this.bindMethod = bindMethod;
     this.state = state;
 }
Пример #18
0
 public ApplyBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
 {
     this.bindMethod = bindMethod;
     this.state = state;
     this.filterBinder = new FilterBinder(bindMethod, state);
 }
Пример #19
0
 /// <summary>
 /// Constructs a FunctionCallBinder with the given method to be used binding the parent token if needed.
 /// </summary>
 /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param>
 /// <param name="state">State of the metadata binding.</param>
 internal FunctionCallBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
     : base(bindMethod, state)
 {
 }
        public void KeyLookupOnNavPropIntegrationTest()
        {
            var state = new BindingState(configuration);
            state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet());
            var metadataBinder = new MetadataBinder(state);
            var binder = new InnerPathTokenBinder(metadataBinder.Bind, state);
            var token = new InnerPathToken("MyPeople", null, new[] { new NamedValue(null, new LiteralToken(123)) });

            var result = binder.BindInnerPathSegment(token);
            result.ShouldBeKeyLookupQueryNode();
        }
Пример #21
0
        public void BindApplyWitMultipleTokensShouldReturnApplyClause()
        {
            var tokens =
                _parser.ParseApply(
                    "groupby((ID, SSN, LifeTime))/aggregate(LifeTime with sum as TotalLife)/groupby((TotalLife))/aggregate(TotalLife with sum as TotalTotalLife)");

            var state = new BindingState(_configuration);
            var metadataBiner = new MetadataBinder(_bindingState);

            var binder = new ApplyBinder(metadataBiner.Bind, _bindingState);
            var actual = binder.BindApply(tokens);

            actual.Should().NotBeNull();
            actual.Transformations.Should().HaveCount(4);

            var transformations = actual.Transformations.ToList();
            var firstGroupBy = transformations[0] as GroupByTransformationNode;
            firstGroupBy.Should().NotBeNull();
            var firstAggregate = transformations[1] as AggregateTransformationNode;
            firstAggregate.Should().NotBeNull();
            var scecondGroupBy = transformations[2] as GroupByTransformationNode;
            scecondGroupBy.Should().NotBeNull();
            var scecondAggregate = transformations[3] as AggregateTransformationNode;
            scecondAggregate.Should().NotBeNull();
        }
Пример #22
0
 /// <summary>
 /// Constructs a EndPathBinder object using the given function to bind parent token.
 /// </summary>
 /// <param name="bindMethod">Method to bind the EndPathToken's parent, if there is one.</param>
 /// <param name="state">State of the metadata binding.</param>am>
 /// <remarks>
 /// Make bindMethod of return type SingleValueQueryNode.
 /// </remarks>
 internal EndPathBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state)
     : base(bindMethod, state)
 {
     this.functionCallBinder = new FunctionCallBinder(bindMethod, state);
 }
Пример #23
0
 /// <summary>
 /// Constructs a KeyBinder object using the given function to bind key values.
 /// </summary>
 /// <param name="keyValueBindMethod">Method to call to bind a value in a key.</param>
 internal KeyBinder(MetadataBinder.QueryTokenVisitor keyValueBindMethod)
 {
     this.keyValueBindMethod = keyValueBindMethod;
 }
Пример #24
0
        /// <summary>
        /// Parses an <paramref name="apply"/> clause on the given <paramref name="elementType"/>, 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="elementType">Type that the apply clause refers to.</param>
        /// <param name="navigationSource">Navigation source that the elements being filtered are from.</param>
        /// <returns>A <see cref="ApplyClause"/> representing the metadata bound apply expression.</returns>
        private static ApplyClause ParseApplyImplementation(string apply, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource)
        {
            ExceptionUtils.CheckArgumentNotNull(configuration, "configuration");
            ExceptionUtils.CheckArgumentNotNull(elementType, "elementType");
            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);
            state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource);
            state.RangeVariables.Push(state.ImplicitRangeVariable);
            MetadataBinder binder = new MetadataBinder(state);
            ApplyBinder applyBinder = new ApplyBinder(binder.Bind, state);
            ApplyClause boundNode = applyBinder.BindApply(applyTokens);

            return boundNode;
        }
Пример #25
0
 /// <summary>
 /// Constructs a LambdaBinder.
 /// </summary>
 /// <param name="bindMethod">Method used to bind a parent token.</param>
 internal LambdaBinder(MetadataBinder.QueryTokenVisitor bindMethod)
 {
     ExceptionUtils.CheckArgumentNotNull(bindMethod, "bindMethod");
     this.bindMethod = bindMethod;
 }
Пример #26
0
        /// <summary>
        /// Parses the <paramref name="search"/> clause, binding
        /// the text into a metadata-bound list of properties to be selected using the provided model.
        /// </summary>
        /// <param name="search">String representation of the search expression from the URI.</param>
        /// <param name="configuration">The configuration used for binding.</param>
        /// <returns>A <see cref="SearchClause"/> representing the metadata bound search expression.</returns>
        private static SearchClause ParseSearchImplementation(string search, ODataUriParserConfiguration configuration)
        {
            ExceptionUtils.CheckArgumentNotNull(configuration, "configuration");
            ExceptionUtils.CheckArgumentNotNull(search, "search");

            SearchParser searchParser = new SearchParser(configuration.Settings.SearchLimit);
            QueryToken queryToken = searchParser.ParseSearch(search);

            // Bind it to metadata
            BindingState state = new BindingState(configuration);
            MetadataBinder binder = new MetadataBinder(state);
            SearchBinder searchBinder = new SearchBinder(binder.Bind);

            return searchBinder.BindSearch(queryToken);
        }
Пример #27
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 ExpandedNavigationSelectItem GenerateExpandItem(ExpandTermToken tokenIn)
        {
            ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn");

            // ensure that we're always dealing with proper V4 syntax
            if (tokenIn.PathToNavProp.NextToken != null && !tokenIn.PathToNavProp.IsNamespaceOrContainerQualified())
            {
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
            }

            PathSegmentToken currentToken = tokenIn.PathToNavProp;

            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.ODataFullName(), currentToken.Identifier));
            }

            IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty;

            if (currentNavProp == null)
            {
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationProperty(currentToken.Identifier, currentLevelEntityType.ODataFullName()));
            }

            if (firstNonTypeToken.NextToken != null)
            {
                // lastly... make sure that, since we're on a NavProp, that the next token isn't null.
                throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath);
            }

            pathSoFar.Add(new NavigationPropertySegment(currentNavProp, /*entitySet*/ null));
            ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar);

            SelectExpandClause subSelectExpand;

            if (tokenIn.ExpandOption != null)
            {
                subSelectExpand = this.GenerateSubExpand(currentNavProp, tokenIn);
            }
            else
            {
                subSelectExpand = BuildDefaultSubExpand();
            }

            subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption);

            IEdmNavigationSource targetNavigationSource = null;

            if (this.NavigationSource != null)
            {
                targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp);
            }

            // 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);
            }

            LevelsClause levelsOption = this.ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp);

            SearchClause searchOption = null;

            if (tokenIn.SearchOption != null)
            {
                MetadataBinder binder       = this.BuildNewMetadataBinder(targetNavigationSource);
                SearchBinder   searchBinder = new SearchBinder(binder.Bind);
                searchOption = searchBinder.BindSearch(tokenIn.SearchOption);
            }

            return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, levelsOption, searchOption, subSelectExpand));
        }