private static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel model, IEdmEntityType entityType, out ODataPathSegment segment) { Debug.Assert(pathToken != null, "pathToken != null"); Debug.Assert(entityType != null, "bindingType != null"); IEnumerable <IEdmFunctionImport> functionImports; var resolver = model as IODataUriParserModelExtensions; if (resolver != null) { functionImports = resolver.FindFunctionImportsByBindingParameterTypeHierarchy(entityType, pathToken.Identifier); } else { functionImports = model.FindFunctionImportsByBindingParameterTypeHierarchy(entityType, pathToken.Identifier); } List <IEdmFunctionImport> possibleFunctions = functionImports.ToList(); if (possibleFunctions.Count <= 0) { segment = null; return(false); } segment = new OperationSegment(possibleFunctions, null /*entitySet*/); return(true); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="entityType">the entity type of the current scope based on type segments.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmEntityType entityType) { ODataPathSegment nextSegment; if (TryBindAsDeclaredProperty(tokenIn, entityType, out nextSegment)) { return(nextSegment); } // for open types, operations must be container-qualified, and because the token type indicates it was not a .-seperated identifier, we should not try to look up operations. if (!entityType.IsOpen || tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, entityType, out nextSegment)) { return(nextSegment); } } if (entityType.IsOpen) { return(new OpenPropertySegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(entityType.FullName(), tokenIn.Identifier)); }
/// <summary> /// Create an expand term using only the property and its subexpand/select /// </summary> /// <param name="pathToNavProp">the path to the navigation property for this expand term</param> /// <param name="selectOption">the sub select for this token</param> /// <param name="expandOption">the sub expand for this token</param> public ExpandTermToken(PathSegmentToken pathToNavProp, SelectToken selectOption, ExpandToken expandOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavProp, "pathToNavigationProperty"); this.pathToNavProp = pathToNavProp; this.filterOption = null; this.orderByOption = null; this.topOption = null; this.skipOption = null; this.selectOption = selectOption; this.expandOption = expandOption; }
/// <summary> /// Create an expand term token /// </summary> /// <param name="pathToNavProp">the nav prop for this expand term</param> /// <param name="filterOption">the filter option for this expand term</param> /// <param name="orderByOption">the orderby option for this expand term</param> /// <param name="topOption">the top option for this expand term</param> /// <param name="skipOption">the skip option for this expand term</param> /// <param name="inlineCountOption">the inlineCountOption for this expand term</param> /// <param name="selectOption">the select option for this expand term</param> /// <param name="expandOption">the expand option for this expand term</param> public ExpandTermToken(PathSegmentToken pathToNavProp, QueryToken filterOption, OrderByToken orderByOption, long?topOption, long?skipOption, InlineCountKind?inlineCountOption, SelectToken selectOption, ExpandToken expandOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavProp, "property"); this.pathToNavProp = pathToNavProp; this.filterOption = filterOption; this.orderByOption = orderByOption; this.topOption = topOption; this.skipOption = skipOption; this.inlineCountOption = inlineCountOption; this.selectOption = selectOption; this.expandOption = expandOption; }
/// <summary> /// Invert the all of the paths in an expandToken, such that they are now in the same order as they are present in the /// base url /// </summary> /// <param name="treeToInvert">the tree to invert paths on</param> /// <returns>a new tree with all of its paths inverted</returns> public static ExpandToken InvertPaths(ExpandToken treeToInvert) { // iterate through each expand term token, and reverse the tree in its path property List <ExpandTermToken> updatedTerms = new List <ExpandTermToken>(); foreach (ExpandTermToken term in treeToInvert.ExpandTerms) { PathReverser pathReverser = new PathReverser(); PathSegmentToken reversedPath = term.PathToNavProp.Accept(pathReverser); ExpandTermToken newTerm = new ExpandTermToken(reversedPath, term.FilterOption, term.OrderByOption, term.TopOption, term.SkipOption, term.InlineCountOption, term.SelectOption, term.ExpandOption); updatedTerms.Add(newTerm); } return(new ExpandToken(updatedTerms)); }
/// <summary> /// Follow any type segments on the path, stopping at the first segment that isn't a type token. /// </summary> /// <param name="firstTypeToken">the first type segment</param> /// <param name="model">the model these types are contained in.</param> /// <param name="maxDepth">the maximum recursive depth</param> /// <param name="currentLevelEntityType">the top level entity type, will be overwritten with the last entity type in the chain</param> /// <param name="firstNonTypeToken">the first non type token in the path</param> /// <returns>A path with type segments added to it.</returns> public static IEnumerable <ODataPathSegment> FollowTypeSegments(PathSegmentToken firstTypeToken, IEdmModel model, int maxDepth, ref IEdmEntityType currentLevelEntityType, out PathSegmentToken firstNonTypeToken) { ExceptionUtils.CheckArgumentNotNull(firstTypeToken, "firstTypeToken"); ExceptionUtils.CheckArgumentNotNull(model, "model"); if (!firstTypeToken.IsNamespaceOrContainerQualified()) { throw new ODataException(ODataErrorStrings.SelectExpandPathBinder_FollowNonTypeSegment(firstTypeToken.Identifier)); } int index = 0; List <ODataPathSegment> pathToReturn = new List <ODataPathSegment>(); PathSegmentToken currentToken = firstTypeToken; while (currentToken.IsNamespaceOrContainerQualified() && currentToken.NextToken != null) { IEdmEntityType previousLevelEntityType = currentLevelEntityType; currentLevelEntityType = UriEdmHelpers.FindTypeFromModel(model, currentToken.Identifier) as IEdmEntityType; if (currentLevelEntityType == null) { // TODO: fix this error message? throw new ODataException(ODataErrorStrings.ExpandItemBinder_CannotFindType(currentToken.Identifier)); } UriEdmHelpers.CheckRelatedTo(previousLevelEntityType, currentLevelEntityType); pathToReturn.Add(new TypeSegment(currentLevelEntityType, /*entitySet*/ null)); index++; currentToken = currentToken.NextToken; if (index >= maxDepth) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_PathTooDeep); } } firstNonTypeToken = currentToken; return(pathToReturn); }
/// <summary> /// Build a wildcard selection item /// </summary> /// <param name="tokenIn">the token to bind to a wildcard</param> /// <param name="model">the model to search for this wildcard</param> /// <param name="item">the new wildcard selection item, if we found one</param> /// <returns>true if we successfully bound to a wildcard, false otherwise</returns> public static bool TryBindAsWildcard(PathSegmentToken tokenIn, IEdmModel model, out SelectItem item) { bool isTypeToken = tokenIn.IsNamespaceOrContainerQualified(); bool wildcard = tokenIn.Identifier.EndsWith("*", StringComparison.Ordinal); IEdmEntityContainer container; if (isTypeToken && wildcard && UriEdmHelpers.TryGetEntityContainer(tokenIn.Identifier.Substring(0, tokenIn.Identifier.LastIndexOf('.')), model, out container)) { item = new ContainerQualifiedWildcardSelectItem(container); return(true); } if (tokenIn.Identifier == "*") { item = new WildcardSelectItem(); return(true); } item = null; return(false); }
private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmEntityType entityType, out ODataPathSegment segment) { IEdmProperty prop = entityType.FindProperty(tokenIn.Identifier); if (prop == null) { segment = null; return(false); } if (prop.PropertyKind == EdmPropertyKind.Structural) { segment = new PropertySegment((IEdmStructuralProperty)prop); return(true); } if (prop.PropertyKind == EdmPropertyKind.Navigation) { segment = new NavigationPropertySegment((IEdmNavigationProperty)prop, null /*TODO set*/); return(true); } throw new ODataException(ODataErrorStrings.SelectExpandBinder_UnknownPropertyType(prop.Name)); }
/// <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> /// internal setter for the next token. /// </summary> /// <param name="nextTokenIn">the next token to set.</param> internal void SetNextToken(PathSegmentToken nextTokenIn) { DebugUtils.CheckNoExternalCallers(); this.nextToken = nextTokenIn; }
/// <summary> /// build this segment token using the next token /// </summary> /// <param name="nextToken">the next token in the path</param> protected PathSegmentToken(PathSegmentToken nextToken) { this.nextToken = nextToken; }
/// <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> /// Build a NonSystemToken /// </summary> /// <param name="identifier">the identifier of this token</param> /// <param name="namedValues">a list of named values for this token</param> /// <param name="nextToken">the next token in the path</param> public NonSystemToken(string identifier, IEnumerable <NamedValue> namedValues, PathSegmentToken nextToken) : base(nextToken) { ExceptionUtils.CheckArgumentNotNull(identifier, "identifier"); this.identifier = identifier; this.namedValues = namedValues; }
/// <summary> /// Build a new System Token /// </summary> /// <param name="identifier">the identifier for this token.</param> /// <param name="nextToken">the next token in the path</param> public SystemToken(string identifier, PathSegmentToken nextToken) : base(nextToken) { ExceptionUtils.CheckArgumentNotNull(identifier, "identifier"); this.identifier = identifier; }