/// <summary> /// add a new expandTermToken into an exisiting token, adding any additional levels and trees along the way. /// </summary> /// <param name="existingToken">the exisiting (already expanded) token</param> /// <param name="newToken">the new (already expanded) token</param> /// <returns>the combined token, or, if the two are mutually exclusive, the same tokens</returns> public static ExpandTermToken CombineTerms(ExpandTermToken existingToken, ExpandTermToken newToken) { Debug.Assert(new PathSegmentTokenEqualityComparer().Equals(existingToken.PathToNavProp, newToken.PathToNavProp), "Paths should be equal."); List <ExpandTermToken> childNodes = CombineChildNodes(existingToken, newToken).ToList(); return(new ExpandTermToken( existingToken.PathToNavProp, existingToken.FilterOption, existingToken.OrderByOption, existingToken.TopOption, existingToken.SkipOption, existingToken.InlineCountOption, existingToken.SelectOption, new ExpandToken(childNodes))); }
/// <summary> /// Combine the child nodes of twoExpandTermTokens into one list of tokens /// </summary> /// <param name="existingToken">the existing token to to</param> /// <param name="newToken">the new token containing terms to add</param> /// <returns>a combined list of the all child nodes of the two tokens.</returns> public static IEnumerable <ExpandTermToken> CombineChildNodes(ExpandTermToken existingToken, ExpandTermToken newToken) { if (existingToken.ExpandOption == null && newToken.ExpandOption == null) { return(new List <ExpandTermToken>()); } var childNodes = new Dictionary <PathSegmentToken, ExpandTermToken>(new PathSegmentTokenEqualityComparer()); if (existingToken.ExpandOption != null) { AddChildOptionsToDictionary(existingToken, childNodes); } if (newToken.ExpandOption != null) { AddChildOptionsToDictionary(newToken, childNodes); } return(childNodes.Values); }
/// <summary> /// Generate a SubExpand based on the current nav property and the curren token /// </summary> /// <param name="currentNavProp">the current navigation property</param> /// <param name="tokenIn">the current token</param> /// <returns>a new SelectExpand clause bound to the current token and nav prop</returns> protected override SelectExpandClause GenerateSubExpand(IEdmNavigationProperty currentNavProp, ExpandTermToken tokenIn) { Debug.Assert(tokenIn.SelectOption == null, "Should not have select on individual expand items for this binder, because the V3 syntax does not support selects within the $expand clause."); ExpandBinder nextLevelBinder = new NonOptionExpandBinder(this.Configuration, currentNavProp.ToEntityType(), this.EntitySet != null ? this.EntitySet.FindNavigationTarget(currentNavProp) : null); return(nextLevelBinder.Bind(tokenIn.ExpandOption)); }
/// <summary> /// Generate an expand item 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 a normalized tree if (tokenIn.PathToNavProp.NextToken != null && !tokenIn.PathToNavProp.IsNamespaceOrContainerQualified()) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingANonNormalizedTree); } PathSegmentToken currentToken = tokenIn.PathToNavProp; IEdmEntityType currentLevelEntityType = this.entityType; List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); PathSegmentToken firstNonTypeToken = currentToken; if (currentToken.IsNamespaceOrContainerQualified()) { pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, ref currentLevelEntityType, out firstNonTypeToken)); } IEdmProperty edmProperty = currentLevelEntityType.FindProperty(firstNonTypeToken.Identifier); if (edmProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.FullName(), currentToken.Identifier)); } IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty; if (currentNavProp == null) { // the server allowed non-navigation, non-stream properties to be expanded, but then ignored them. if (this.Settings.UseWcfDataServicesServerBehavior && !edmProperty.Type.IsStream()) { return(null); } throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationProperty(currentToken.Identifier, currentLevelEntityType.FullName())); } 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); IEdmEntitySet targetEntitySet = null; if (this.entitySet != null) { targetEntitySet = this.entitySet.FindNavigationTarget(currentNavProp); } // call MetadataBinder to build the filter clause FilterClause filterOption = null; if (tokenIn.FilterOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetEntitySet); 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.OrderByOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetEntitySet); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); orderbyOption = orderByBinder.BindOrderBy(binder.BindingState, new OrderByToken[] { tokenIn.OrderByOption }); } return(new ExpandedNavigationSelectItem(pathToNavProp, targetEntitySet, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.InlineCountOption, subSelectExpand)); }
/// <summary> /// Generate a SubExpand based on the current nav property and the curren token /// </summary> /// <param name="currentNavProp">the current navigation property</param> /// <param name="tokenIn">the current token</param> /// <returns>a new SelectExpand clause bound to the current token and nav prop</returns> protected abstract SelectExpandClause GenerateSubExpand(IEdmNavigationProperty currentNavProp, ExpandTermToken tokenIn);
/// <summary> /// Expand all the PathTokens in a particular term into their own separate terms. /// </summary> /// <param name="termToExpand">the term to expand</param> /// <returns>a new ExpandTermToken with each PathToken at its own level.</returns> public static ExpandTermToken BuildSubExpandTree(ExpandTermToken termToExpand) { // walk up the path tree on the property token, adding new expand clauses for each level if (termToExpand.PathToNavProp.NextToken == null) { return(termToExpand); } PathSegmentToken currentProperty = termToExpand.PathToNavProp; // if we find a type token, then the current property becomes a path instead, // so that we can follow the chain of derived types from the base. // the path can only consist of NavProps or TypeSegments, we can // simply walk the tree of the current property until we find a non // type segment, then break its next link, then return the new path chain which // becomes our current property PathSegmentToken currentToken = currentProperty; while (currentToken.IsNamespaceOrContainerQualified()) { currentToken = currentToken.NextToken; if (currentToken == null) { throw new ODataException(ODataErrorStrings.ExpandTreeNormalizer_NonPathInPropertyChain); } } // get a pointer to the next property so that we can continue down the list. PathSegmentToken nextProperty = currentToken.NextToken; // chop off the next pointer to end this chain. currentToken.SetNextToken(null); ExpandToken subExpand; if (nextProperty != null) { var subExpandTermToken = new ExpandTermToken( nextProperty, termToExpand.FilterOption, termToExpand.OrderByOption, termToExpand.TopOption, termToExpand.SkipOption, termToExpand.InlineCountOption, termToExpand.SelectOption, /*expandOption*/ null); ExpandTermToken subExpandToken = BuildSubExpandTree(subExpandTermToken); subExpand = new ExpandToken(new[] { subExpandToken }); } else { subExpand = new ExpandToken(new ExpandTermToken[0]); } return(new ExpandTermToken( currentProperty, termToExpand.FilterOption, termToExpand.OrderByOption, termToExpand.TopOption, termToExpand.SkipOption, termToExpand.InlineCountOption, termToExpand.SelectOption, subExpand)); }
/// <summary> /// Adds the expand token to the dictionary or combines it with an existing or combines it with another existing token with an equivalent path. /// </summary> /// <param name="combinedTerms">The combined terms dictionary.</param> /// <param name="expandedTerm">The expanded term to add or combine.</param> private static void AddOrCombine(IDictionary <PathSegmentToken, ExpandTermToken> combinedTerms, ExpandTermToken expandedTerm) { ExpandTermToken existingTerm; if (combinedTerms.TryGetValue(expandedTerm.PathToNavProp, out existingTerm)) { combinedTerms[expandedTerm.PathToNavProp] = CombineTerms(expandedTerm, existingTerm); } else { combinedTerms.Add(expandedTerm.PathToNavProp, expandedTerm); } }
/// <summary> /// Visits an ExpandTermToken /// </summary> /// <param name="tokenIn">The ExpandTermToken to visit</param> /// <returns>A QueryNode bound to this ExpandTermToken</returns> public virtual T Visit(ExpandTermToken tokenIn) { throw new NotImplementedException(); }
/// <summary> /// Generate a SubExpand based on the current nav property and the curren token /// </summary> /// <param name="currentNavProp">the current navigation property</param> /// <param name="tokenIn">the current token</param> /// <returns>a new SelectExpand clause bound to the current token and nav prop</returns> protected override SelectExpandClause GenerateSubExpand(IEdmNavigationProperty currentNavProp, ExpandTermToken tokenIn) { ExpandBinder nextLevelBinder = new ExpandOptionExpandBinder(this.Configuration, currentNavProp.ToEntityType(), this.EntitySet != null ? this.EntitySet.FindNavigationTarget(currentNavProp) : null); return(nextLevelBinder.Bind(tokenIn.ExpandOption)); }