/// <summary> /// Parse a full Uri into its contingent parts with semantic meaning attached to each part. /// See <see cref="ODataUri"/>. /// </summary> /// <returns>An <see cref="ODataUri"/> representing the full uri.</returns> public ODataUri ParseUri() { ExceptionUtils.CheckArgumentNotNull(this.configuration.Model, "model"); ExceptionUtils.CheckArgumentNotNull(this.uri, "uri"); ODataPath path = this.ParsePath(); SelectExpandClause selectExpand = this.ParseSelectAndExpand(); FilterClause filter = this.ParseFilter(); OrderByClause orderBy = this.ParseOrderBy(); SearchClause search = this.ParseSearch(); ApplyClause apply = this.ParseApply(); ComputeClause compute = this.ParseCompute(); long? top = this.ParseTop(); long? skip = this.ParseSkip(); bool? count = this.ParseCount(); string skipToken = this.ParseSkipToken(); string deltaToken = this.ParseDeltaToken(); // TODO: check it shouldn't be empty List <QueryNode> boundQueryOptions = new List <QueryNode>(); ODataUri odataUri = new ODataUri(this.ParameterAliasValueAccessor, path, boundQueryOptions, selectExpand, filter, orderBy, search, apply, skip, top, count, compute); odataUri.ServiceRoot = this.serviceRoot; odataUri.SkipToken = skipToken; odataUri.DeltaToken = deltaToken; return(odataUri); }
/// <summary> /// Create an expand item, using a navigationProperty, its entity set, and any expand options. /// </summary> /// <param name="pathToNavigationProperty">the path to the navigation property for this expand item, including any type segments</param> /// <param name="navigationSource">the navigation source for this expand level.</param> /// <param name="filterOption">A filter clause for this expand (can be null)</param> /// <param name="orderByOption">An Orderby clause for this expand (can be null)</param> /// <param name="topOption">A top clause for this expand (can be null)</param> /// <param name="skipOption">A skip clause for this expand (can be null)</param> /// <param name="countOption">An query count clause for this expand (can be null)</param> /// <param name="searchOption">A search clause for this expand (can be null)</param> /// <param name="computeOption">A compute clause for this expand (can be null)</param> /// <param name="applyOption">A apply clause for this expand (can be null)</param> /// <exception cref="System.ArgumentNullException">Throws if input pathToNavigationProperty is null.</exception> public ExpandedReferenceSelectItem( ODataExpandPath pathToNavigationProperty, IEdmNavigationSource navigationSource, FilterClause filterOption, OrderByClause orderByOption, long?topOption, long?skipOption, bool?countOption, SearchClause searchOption, ComputeClause computeOption, ApplyClause applyOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavigationProperty, "pathToNavigationProperty"); this.PathToNavigationProperty = pathToNavigationProperty; this.NavigationSource = navigationSource; this.FilterOption = filterOption; this.OrderByOption = orderByOption; this.TopOption = topOption; this.SkipOption = skipOption; this.CountOption = countOption; this.SearchOption = searchOption; this.ComputeOption = computeOption; this.ApplyOption = applyOption; }
/// <summary> /// Create an expand item, using a navigationProperty, its entity set, and any expand options. /// </summary> /// <param name="pathToNavigationProperty">the path to the navigation property for this expand item, including any type segments</param> /// <param name="navigationSource">the navigation source for this expand level.</param> /// <param name="filterOption">A filter clause for this expand (can be null)</param> /// <param name="orderByOption">An Orderby clause for this expand (can be null)</param> /// <param name="topOption">A top clause for this expand (can be null)</param> /// <param name="skipOption">A skip clause for this expand (can be null)</param> /// <param name="countOption">An query count clause for this expand (can be null)</param> /// <param name="searchOption">A search clause for this expand (can be null)</param> /// <param name="computeOption">A compute clause for this expand (can be null)</param> /// <exception cref="System.ArgumentNullException">Throws if input pathToNavigationProperty is null.</exception> public ExpandedReferenceSelectItem( ODataExpandPath pathToNavigationProperty, IEdmNavigationSource navigationSource, FilterClause filterOption, OrderByClause orderByOption, long?topOption, long?skipOption, bool?countOption, SearchClause searchOption, ComputeClause computeOption) : this(pathToNavigationProperty, navigationSource, filterOption, orderByOption, topOption, skipOption, countOption, searchOption, computeOption, null) { }
/// <summary> /// Parses the <paramref name="compute"/> clause, binding /// the text into a metadata-bound list of compuations using the provided model. /// </summary> /// <param name="compute">String representation of the compute expression from the URI.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="ComputeClause"/> representing the metadata bound compute expression.</returns> private ComputeClause ParseComputeImplementation(string compute, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(compute, "compute"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); ComputeToken computeToken = expressionParser.ParseCompute(compute); // Bind it to metadata BindingState state = CreateBindingState(configuration, odataPathInfo); MetadataBinder binder = new MetadataBinder(state); ComputeBinder computeBinder = new ComputeBinder(binder.Bind); ComputeClause boundNode = computeBinder.BindCompute(computeToken); return(boundNode); }
/// <summary> /// Parses a compute clause on the given full Uri, binding /// the text into semantic nodes using the constructed mode. /// </summary> /// <returns>A <see cref="ComputeClause"/> representing the computed properties.</returns> public ComputeClause ParseCompute() { if (this.computeClause != null) { return(this.computeClause); } string computeQuery; if (!this.TryGetQueryOption(UriQueryConstants.ComputeQueryOption, out computeQuery) || string.IsNullOrEmpty(computeQuery) || this.targetEdmType == null) { return(null); } this.computeClause = ParseComputeImplementation(computeQuery, this.Configuration, this.odataPathInfo); return(this.computeClause); }
/// <summary> /// Create an expand item, using a navigationProperty, its entity set, and any expand options. /// </summary> /// <param name="pathToNavigationProperty">the path to the navigation property for this expand item, including any type segments</param> /// <param name="navigationSource">the navigation source for this expand level.</param> /// <param name="selectAndExpand">This level select and any sub expands for this expand item.</param> /// <param name="filterOption">A filter clause for this expand (can be null)</param> /// <param name="orderByOption">An Orderby clause for this expand (can be null)</param> /// <param name="topOption">A top clause for this expand (can be null)</param> /// <param name="skipOption">A skip clause for this expand (can be null)</param> /// <param name="countOption">An query count clause for this expand (can be null)</param> /// <param name="searchOption">An levels clause for this expand (can be null)</param> /// <param name="levelsOption">An levels clause for this expand (can be null)</param> /// <param name="computeOption">A compute clause for this expand (can be null)</param> /// <exception cref="System.ArgumentNullException">Throws if input pathToNavigationProperty is null.</exception> public ExpandedNavigationSelectItem( ODataExpandPath pathToNavigationProperty, IEdmNavigationSource navigationSource, SelectExpandClause selectAndExpand, FilterClause filterOption, OrderByClause orderByOption, long?topOption, long?skipOption, bool?countOption, SearchClause searchOption, LevelsClause levelsOption, ComputeClause computeOption) : base(pathToNavigationProperty, navigationSource, filterOption, orderByOption, topOption, skipOption, countOption, searchOption, computeOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavigationProperty, "pathToNavigationProperty"); this.SelectAndExpand = selectAndExpand; this.LevelsOption = levelsOption; }
/// <summary> /// Parses the <paramref name="compute"/> clause, binding /// the text into a metadata-bound list of compuations using the provided model. /// </summary> /// <param name="compute">String representation of the compute expression from the URI.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="odataPathInfo">The path info from Uri path.</param> /// <returns>A <see cref="ComputeClause"/> representing the metadata bound compute expression.</returns> private static ComputeClause ParseComputeImplementation(string compute, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(compute, "compute"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); ComputeToken computeToken = expressionParser.ParseCompute(compute); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(odataPathInfo.TargetEdmType.ToTypeReference(), odataPathInfo.TargetNavigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); ComputeBinder computeBinder = new ComputeBinder(binder.Bind); ComputeClause boundNode = computeBinder.BindCompute(computeToken); return(boundNode); }
private static HashSet <EndPathToken> GetGeneratedProperties(ComputeClause computeOption, ApplyClause applyOption) { HashSet <EndPathToken> generatedProperties = null; if (applyOption != null) { generatedProperties = applyOption.GetLastAggregatedPropertyNames(); } if (computeOption != null) { var computedProperties = new HashSet <EndPathToken>(computeOption.ComputedItems.Select(i => new EndPathToken(i.Alias, null))); if (generatedProperties == null) { generatedProperties = computedProperties; } else { generatedProperties.UnionWith(computedProperties); } } return(generatedProperties); }
/// <summary> /// Constructs a <see cref="PathSelectItem"/> to indicate that a specific path is selected. /// </summary> /// <param name="selectedPath">The selected path.</param> /// <param name="navigationSource">The navigation source for this select item.</param> /// <param name="selectAndExpand">This sub select and sub expand for this select item.</param> /// <param name="filterOption">A filter clause for this select (can be null).</param> /// <param name="orderByOption">An Orderby clause for this select (can be null).</param> /// <param name="topOption">A top clause for this select (can be null).</param> /// <param name="skipOption">A skip clause for this select (can be null).</param> /// <param name="countOption">A count clause for this select (can be null).</param> /// <param name="searchOption">A search clause for this select (can be null).</param> /// <param name="computeOption">A compute clause for this expand (can be null).</param> /// <exception cref="ArgumentNullException">Throws if the input selectedPath is null.</exception> public PathSelectItem(ODataSelectPath selectedPath, IEdmNavigationSource navigationSource, SelectExpandClause selectAndExpand, FilterClause filterOption, OrderByClause orderByOption, long?topOption, long?skipOption, bool?countOption, SearchClause searchOption, ComputeClause computeOption) { ExceptionUtils.CheckArgumentNotNull(selectedPath, "selectedPath"); SelectedPath = selectedPath; NavigationSource = navigationSource; SelectAndExpand = selectAndExpand; FilterOption = filterOption; OrderByOption = orderByOption; TopOption = topOption; SkipOption = skipOption; CountOption = countOption; SearchOption = searchOption; ComputeOption = computeOption; }
/// <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 List <ODataPathSegment> parsedPath = new List <ODataPathSegment>(this.parsedSegments); parsedPath.AddRange(pathSoFar); IEdmNavigationSource targetNavigationSource = null; if (this.NavigationSource != null) { IEdmPathExpression bindingPath; targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp, BindingPathHelper.MatchBindingPath, parsedPath, out bindingPath); } NavigationPropertySegment navSegment = new NavigationPropertySegment(currentNavProp, targetNavigationSource); pathSoFar.Add(navSegment); parsedPath.Add(navSegment); // Add the navigation property segment to parsed segments for future usage. ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar); // $apply ApplyClause applyOption = BindApply(tokenIn.ApplyOptions, targetNavigationSource); // $compute ComputeClause computeOption = BindCompute(tokenIn.ComputeOption, targetNavigationSource); var generatedProperties = GetGeneratedProperties(computeOption, applyOption); bool collapsed = applyOption?.Transformations.Any(t => t.Kind == TransformationNodeKind.Aggregate || t.Kind == TransformationNodeKind.GroupBy) ?? false; // $filter FilterClause filterOption = BindFilter(tokenIn.FilterOption, targetNavigationSource, null, generatedProperties, collapsed); // $orderby OrderByClause orderbyOption = BindOrderby(tokenIn.OrderByOptions, targetNavigationSource, null, generatedProperties, collapsed); // $search SearchClause searchOption = BindSearch(tokenIn.SearchOption, targetNavigationSource, null); if (isRef) { return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, computeOption, applyOption)); } // $select & $expand SelectExpandClause subSelectExpand = BindSelectExpand(tokenIn.ExpandOption, tokenIn.SelectOption, parsedPath, targetNavigationSource, null, generatedProperties, collapsed); // $levels LevelsClause levelsOption = ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp); return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption, computeOption, applyOption)); }
/// <summary> /// Generate a select item <see cref="SelectItem"/> based on a <see cref="SelectTermToken"/>. /// for example: abc/efg($count=true;$filter=....;$top=1) /// </summary> /// <param name="tokenIn">the select term token to visit</param> /// <returns>the select item for this select term token.</returns> private SelectItem GenerateSelectItem(SelectTermToken tokenIn) { ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn"); ExceptionUtils.CheckArgumentNotNull(tokenIn.PathToProperty, "pathToProperty"); VerifySelectedPath(tokenIn); SelectItem newSelectItem; if (ProcessWildcardTokenPath(tokenIn, out newSelectItem)) { return(newSelectItem); } IList <ODataPathSegment> selectedPath = ProcessSelectTokenPath(tokenIn.PathToProperty); Debug.Assert(selectedPath.Count > 0); // Navigation property should be the last segment in select path. if (VerifySelectedNavigationProperty(selectedPath, tokenIn)) { return(new PathSelectItem(new ODataSelectPath(selectedPath))); } // We should use the "NavigationSource" at this level for the next level binding. IEdmNavigationSource targetNavigationSource = this.NavigationSource; ODataPathSegment lastSegment = selectedPath.Last(); IEdmType targetElementType = lastSegment.TargetEdmType; IEdmCollectionType collection = targetElementType as IEdmCollectionType; if (collection != null) { targetElementType = collection.ElementType.Definition; } IEdmTypeReference elementTypeReference = targetElementType.ToTypeReference(); // $compute ComputeClause compute = BindCompute(tokenIn.ComputeOption, targetNavigationSource, elementTypeReference); HashSet <EndPathToken> generatedProperties = GetGeneratedProperties(compute, null); // $filter FilterClause filter = BindFilter(tokenIn.FilterOption, targetNavigationSource, elementTypeReference, generatedProperties); // $orderby OrderByClause orderBy = BindOrderby(tokenIn.OrderByOptions, targetNavigationSource, elementTypeReference, generatedProperties); // $search SearchClause search = BindSearch(tokenIn.SearchOption, targetNavigationSource, elementTypeReference); // $select List <ODataPathSegment> parsedPath = new List <ODataPathSegment>(this.parsedSegments); parsedPath.AddRange(selectedPath); SelectExpandClause selectExpand = BindSelectExpand(null, tokenIn.SelectOption, parsedPath, targetNavigationSource, elementTypeReference, generatedProperties); return(new PathSelectItem(new ODataSelectPath(selectedPath), targetNavigationSource, selectExpand, filter, orderBy, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, search, compute)); }
/// <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 ExceptionUtil.CreatePropertyNotFoundException(currentToken.Identifier, currentLevelEntityType.FullTypeName()); } 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); } ComputeClause computeOption = null; if (tokenIn.ComputeOption != null) { MetadataBinder binder = this.BuildNewMetadataBinder(targetNavigationSource); ComputeBinder computeBinder = new ComputeBinder(binder.Bind); computeOption = computeBinder.BindCompute(tokenIn.ComputeOption); } if (isRef) { return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, computeOption)); } SelectExpandClause subSelectExpand; if (tokenIn.ExpandOption != null) { subSelectExpand = this.GenerateSubExpand(tokenIn); } else { subSelectExpand = BuildDefaultSubExpand(); } subSelectExpand = this.DecorateExpandWithSelect(subSelectExpand, currentNavProp, tokenIn.SelectOption); LevelsClause levelsOption = ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp); return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption, computeOption)); }