/// <summary> /// Parses a select or expand term into a query token /// </summary> /// <returns>parsed query token</returns> private PathSegmentToken ParseSelectExpandProperty() { PathSegmentToken token = null; int currentDepth = 0; do { currentDepth++; if (currentDepth > this.maxDepth) { throw new ODataException(ODataErrorStrings.UriQueryExpressionParser_TooDeep); } this.Lexer.NextToken(); // allow a single trailing slash for backwards compatibility with the WCF DS Server parser. if (currentDepth > 1 && this.Lexer.CurrentToken.Kind == ExpressionTokenKind.End) { break; } token = this.ParseNext(token); }while (this.Lexer.CurrentToken.Kind == ExpressionTokenKind.Slash); return(token); }
/// <summary> /// Parses a single term in a comma seperated list of things to expand. /// </summary> /// <returns>A token representing thing to expand.</returns> private ExpandTermToken ParseSingleExpandTerm() { this.isSelect = false; var termParser = new SelectExpandTermParser(this.lexer, this.MaxPathDepth, this.isSelect); PathSegmentToken pathToken = termParser.ParseTerm(); string optionsText = null; if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { optionsText = this.lexer.AdvanceThroughBalancedParentheticalExpression(); // Move lexer to what is after the parenthesis expression. Now CurrentToken will be the next thing. this.lexer.NextToken(); } if (expandOptionParser == null) { expandOptionParser = new ExpandOptionParser(this.maxRecursiveDepth, enableCaseInsensitiveBuiltinIdentifier) { MaxFilterDepth = MaxFilterDepth, MaxOrderByDepth = MaxOrderByDepth, MaxSearchDepth = MaxSearchDepth }; } return(this.expandOptionParser.BuildExpandTermToken(pathToken, optionsText)); }
/// <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); if (isTypeToken && wildcard) { string namespaceName = tokenIn.Identifier.Substring(0, tokenIn.Identifier.Length - 2); if (model.DeclaredNamespaces.Any(declaredNamespace => declaredNamespace.Equals(namespaceName, StringComparison.Ordinal))) { item = new NamespaceQualifiedWildcardSelectItem(namespaceName); return true; } } if (tokenIn.Identifier == "*") { item = new WildcardSelectItem(); return true; } item = null; return false; }
/// <summary> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousSegment">Previously parsed PathSegmentToken, or null if this is the first token.</param> /// <returns>A parsed PathSegmentToken representing the next segment in this path.</returns> private PathSegmentToken ParseSegment(PathSegmentToken previousSegment) { if (this.lexer.CurrentToken.Text.StartsWith("$", StringComparison.CurrentCulture)) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.lexer.CurrentToken.Text, this.lexer.ExpressionText)); } string propertyName; if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.lexer.ReadDottedIdentifier(this.isSelect); } else if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.lexer.Position)); } propertyName = this.lexer.CurrentToken.Text; this.lexer.NextToken(); } else { propertyName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); } return(new NonSystemToken(propertyName, null, previousSegment)); }
/// <summary> /// Parses a segment; a expression that is followed by a slash. /// </summary> /// <param name="parent">The parent of the segment node.</param> /// <returns>The lexical token representing the segment.</returns> private PathSegmentToken ParseSegment(PathSegmentToken parent) { string propertyName; if (this.Lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.Lexer.ReadDottedIdentifier(this.isSelect); } else if (this.Lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { if (this.Lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.Lexer.Position)); } propertyName = this.Lexer.CurrentToken.Text; this.Lexer.NextToken(); } else { propertyName = this.Lexer.CurrentToken.GetIdentifier(); this.Lexer.NextToken(); } return(new NonSystemToken(propertyName, null, parent)); }
/// <summary> /// Follow a chain of structrual properties until we hit a non-structural property /// </summary> /// <param name="firstStructuralProperty">the first structural property we hit</param> /// <param name="firstNonStructuralProperty">the first non structural property we hit</param> /// <returns>a comma separated list of structural properties</returns> private string WriteNextStructuralProperties(PathSegmentToken firstStructuralProperty, out PathSegmentToken firstNonStructuralProperty) { firstNonStructuralProperty = firstStructuralProperty; string stringToWrite = ""; while (firstNonStructuralProperty.IsStructuralProperty) { if (firstNonStructuralProperty.NextToken != null) { if (firstNonStructuralProperty.NextToken.IsStructuralProperty) { stringToWrite += firstNonStructuralProperty.Identifier + ","; } else { stringToWrite += firstNonStructuralProperty.Identifier; } firstNonStructuralProperty = firstNonStructuralProperty.NextToken; } else { stringToWrite += firstNonStructuralProperty.Identifier; firstNonStructuralProperty = null; return(stringToWrite); } } return(stringToWrite); }
public void NormalizeTreeResultsInReversedPath() { // Arrange: $select=1/2/3 NonSystemToken endPath = new NonSystemToken("3", null, new NonSystemToken("2", null, new NonSystemToken("1", null, null))); Assert.Equal("3/2/1", endPath.ToPathString()); SelectToken selectToken = new SelectToken(new SelectTermToken[] { new SelectTermToken(endPath) }); // Act SelectToken normalizedToken = SelectTreeNormalizer.NormalizeSelectTree(selectToken); // Assert Assert.NotNull(normalizedToken); SelectTermToken updatedSegmentToken = Assert.Single(normalizedToken.SelectTerms); PathSegmentToken segmentToken = updatedSegmentToken.PathToProperty; segmentToken.ShouldBeNonSystemToken("1") .NextToken.ShouldBeNonSystemToken("2") .NextToken.ShouldBeNonSystemToken("3"); Assert.Equal("1/2/3", segmentToken.ToPathString()); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the 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, IEdmStructuredType edmType) { ODataPathSegment nextSegment; if (TryBindAsDeclaredProperty(tokenIn, edmType, out nextSegment)) { return(nextSegment); } // 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 (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return(nextSegment); } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return(null); } } if (edmType.IsOpen) { return(new OpenPropertySegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.ODataFullName(), tokenIn.Identifier)); }
/// <summary> /// Get the path string for a path segment token. /// </summary> /// <param name="head">The head of the path</param> /// <returns>The path string.</returns> public static string ToPathString(this PathSegmentToken head) { StringBuilder sb = new StringBuilder(); PathSegmentToken curr = head; while (curr != null) { sb.Append(curr.Identifier); NonSystemToken nonSystem = curr as NonSystemToken; if (nonSystem != null && nonSystem.NamedValues != null) { sb.Append("("); sb.Append(string.Join(",", nonSystem.NamedValues.Select(c => c.Name + "=" + c.Value.Value))); sb.Append(")"); } curr = curr.NextToken; if (curr != null) { sb.Append("/"); } } return(sb.ToString()); }
/// <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 ExpandToken NormalizePaths(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); // we also need to call the select token normalizer for this level to reverse the select paths SelectToken newSelectToken = term.SelectOption; if (term.SelectOption != null) { SelectTreeNormalizer selectTreeNormalizer = new SelectTreeNormalizer(); newSelectToken = selectTreeNormalizer.NormalizeSelectTree(term.SelectOption); } ExpandToken subExpandTree; if (term.ExpandOption != null) { subExpandTree = this.NormalizePaths(term.ExpandOption); } else { subExpandTree = null; } ExpandTermToken newTerm = new ExpandTermToken(reversedPath, term.FilterOption, term.OrderByOptions, term.TopOption, term.SkipOption, term.CountQueryOption, term.LevelsOption, term.SearchOption, newSelectToken, subExpandTree); updatedTerms.Add(newTerm); } return(new ExpandToken(updatedTerms)); }
/// <summary> /// Initializes a new instance of <see cref="SelectExpandTermToken"/> class. /// </summary> /// <param name="pathToProperty">the path to property for this select or expand term</param> /// <param name="filterOption">the filter option for this select or expand term</param> /// <param name="orderByOptions">the orderby options for this select or expand term</param> /// <param name="topOption">the top option for this select or expand term</param> /// <param name="skipOption">the skip option for this select or expand term</param> /// <param name="countQueryOption">the query count option for this select or expand term</param> /// <param name="searchOption">the search option for this select or expand term</param> /// <param name="selectOption">the select option for this select or expand term</param> /// <param name="expandOption">the expand option for this select or expand term</param> /// <param name="computeOption">the compute option for this select or expand term.</param> protected SelectExpandTermToken( PathSegmentToken pathToProperty, QueryToken filterOption, IEnumerable <OrderByToken> orderByOptions, long?topOption, long?skipOption, bool?countQueryOption, QueryToken searchOption, SelectToken selectOption, ExpandToken expandOption, ComputeToken computeOption) { ExceptionUtils.CheckArgumentNotNull(pathToProperty, "property"); PathToProperty = pathToProperty; FilterOption = filterOption; OrderByOptions = orderByOptions; TopOption = topOption; SkipOption = skipOption; CountQueryOption = countQueryOption; SearchOption = searchOption; SelectOption = selectOption; ExpandOption = expandOption; ComputeOption = computeOption; }
/// <summary> /// Create an expand term token /// </summary> /// <param name="pathToNavigationProp">the nav prop for this expand term</param> /// <param name="filterOption">the filter option for this expand term</param> /// <param name="orderByOptions">the orderby options 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="countQueryOption">the query count option for this expand term</param> /// <param name="levelsOption">the levels option for this expand term</param> /// <param name="searchOption">the search option 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> /// <param name="computeOption">the compute option for this expand term.</param> public ExpandTermToken( PathSegmentToken pathToNavigationProp, QueryToken filterOption, IEnumerable <OrderByToken> orderByOptions, long?topOption, long?skipOption, bool?countQueryOption, long?levelsOption, QueryToken searchOption, SelectToken selectOption, ExpandToken expandOption, ComputeToken computeOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavigationProp, "property"); this.pathToNavigationProp = pathToNavigationProp; this.filterOption = filterOption; this.orderByOptions = orderByOptions; this.topOption = topOption; this.skipOption = skipOption; this.countQueryOption = countQueryOption; this.levelsOption = levelsOption; this.searchOption = searchOption; this.selectOption = selectOption; this.computeOption = computeOption; this.expandOption = expandOption; }
/// <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); if (isTypeToken && wildcard) { string namespaceName = tokenIn.Identifier.Substring(0, tokenIn.Identifier.Length - 2); if (model.DeclaredNamespaces.Any(declaredNamespace => declaredNamespace.Equals(namespaceName, StringComparison.Ordinal))) { item = new NamespaceQualifiedWildcardSelectItem(namespaceName); return(true); } } if (tokenIn.Identifier == "*") { item = new WildcardSelectItem(); return(true); } item = null; return(false); }
public static AndConstraint <NonSystemToken> ShouldBeNonSystemToken(this PathSegmentToken token, string tokenIdentifier) { token.Should().BeOfType <NonSystemToken>(); NonSystemToken nonSystemToken = token.As <NonSystemToken>(); nonSystemToken.Identifier.Should().Be(tokenIdentifier); return(new AndConstraint <NonSystemToken>(nonSystemToken)); }
public static AndConstraint <SystemToken> ShouldBeSystemToken(this PathSegmentToken token, string tokenIdentifier) { token.Should().BeOfType <SystemToken>(); SystemToken systemToken = token.As <SystemToken>(); systemToken.Identifier.Should().Be(tokenIdentifier); return(new AndConstraint <SystemToken>(systemToken)); }
internal static string ToDebugString(this PathSegmentToken token) { if (token == null) { return("(null)"); } return(token.Identifier); }
public static NonSystemToken ShouldBeNonSystemToken(this PathSegmentToken token, string tokenIdentifier) { Assert.NotNull(token); NonSystemToken nonSystemToken = Assert.IsType <NonSystemToken>(token); Assert.Equal(tokenIdentifier, nonSystemToken.Identifier); return(nonSystemToken); }
public static SystemToken ShouldBeSystemToken(this PathSegmentToken token, string tokenIdentifier) { Assert.NotNull(token); SystemToken systemToken = Assert.IsType <SystemToken>(token); Assert.Equal(tokenIdentifier, systemToken.Identifier); return(systemToken); }
/// <summary> /// Add a new ParameterExpression to the stack. /// </summary> /// <param name="pe">The parameter expression to add.</param> public void PushParamExpression(ParameterExpression pe) { PathSegmentToken basePath = expandPaths.LastOrDefault(); basePaths.Add(pe, basePath); expandPaths.Remove(basePath); parameterExpressions.Push(pe); }
public void ReversePathWorksWithSingleSegment() { // $expand=1 PathReverser pathReverser = new PathReverser(); PathSegmentToken nonReversedPath = new NonSystemToken("1", null, null); PathSegmentToken reversedPath = nonReversedPath.Accept(pathReverser); reversedPath.ShouldBeNonSystemToken("1").NextToken.Should().BeNull(); }
public void NullHeadReturnsNull() { // Arrange & Act PathSegmentToken head = null; PathSegmentToken reversedPath = head.Reverse(); // Assert Assert.Null(reversedPath); }
public void ReversePathWorksWithATypeToken() { // $expand=Fully.Qualified.Namespace/1 PathReverser pathReverser = new PathReverser(); PathSegmentToken nonReversedPath = new NonSystemToken("1", null, new NonSystemToken("Fully.Qualified.Namespace", null, null)); PathSegmentToken reversedPath = nonReversedPath.Accept(pathReverser); reversedPath.ShouldBeNonSystemToken("Fully.Qualified.Namespace").NextToken.ShouldBeNonSystemToken("1"); }
public void ReversePathWorksWithStarToken() { // $expand=1/* PathReverser pathReverser = new PathReverser(); PathSegmentToken nonReversedPath = new NonSystemToken("*", null, new NonSystemToken("1", null, null)); PathSegmentToken reversedPath = nonReversedPath.Accept(pathReverser); reversedPath.ShouldBeNonSystemToken("1").NextToken.ShouldBeNonSystemToken("*"); }
/// <summary> /// Build the list of expand options /// Depends on whether options are allowed or not. /// </summary> /// <param name="isInnerTerm">is this an inner expand term</param> /// <param name="pathToken">the current level token, as a PathToken</param> /// <returns>An expand term token based on the path token.</returns> internal override ExpandTermToken BuildExpandTermToken(bool isInnerTerm, PathSegmentToken pathToken) { DebugUtils.CheckNoExternalCallers(); if (this.IsNotEndOfTerm(false)) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.Lexer.ExpressionText)); } return(new ExpandTermToken(pathToken)); }
/// <summary> /// Parses a single term in a comma seperated list of things to expand. /// </summary> /// <param name="isInnerTerm">is this an inner or outer term.</param> /// <returns>A token representing thing to expand.</returns> public ExpandTermToken ParseSingleExpandTerm(bool isInnerTerm) { this.isSelect = false; this.RecurseEnter(); PathSegmentToken property = this.ParseSelectExpandProperty(); this.RecurseLeave(); return(this.BuildExpandTermToken(isInnerTerm, property)); }
/// <summary> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousToken">Previously parsed QueryToken, or null if this is the first token.</param> /// <returns>A parsed QueryToken representing the next part of the expression.</returns> private PathSegmentToken ParseNext(PathSegmentToken previousToken) { if (this.Lexer.CurrentToken.Text.StartsWith("$", StringComparison.CurrentCulture)) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.Lexer.CurrentToken.Text, this.Lexer.ExpressionText)); } else { return(this.ParseSegment(previousToken)); } }
/// <summary> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousSegment">Previously parsed PathSegmentToken, or null if this is the first token.</param> /// <param name="allowRef">Whether the $ref operation is valid in this token.</param> /// <returns>A parsed PathSegmentToken representing the next segment in this path.</returns> private PathSegmentToken ParseSegment(PathSegmentToken previousSegment, bool allowRef) { // TODO $count is defined in specification for expand, it is not supported now. Also note $count is not supported with star as expand option. if (this.lexer.CurrentToken.Text.StartsWith("$", StringComparison.Ordinal) && (!allowRef || this.lexer.CurrentToken.Text != UriQueryConstants.RefSegment)) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.lexer.CurrentToken.Text, this.lexer.ExpressionText)); } // Some check here to throw exception, both prop1/*/prop2 and */$ref/prop will throw exception, both are for $expand cases if (!isSelect) { if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.Star && this.lexer.CurrentToken.GetIdentifier() != UriQueryConstants.RefSegment) { // Star can only be followed with $ref throw new ODataException(ODataErrorStrings.ExpressionToken_OnlyRefAllowWithStarInExpand); } else if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.RefSegment) { // $ref should not have more property followed. throw new ODataException(ODataErrorStrings.ExpressionToken_NoPropAllowedAfterRef); } } string propertyName; if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.lexer.ReadDottedIdentifier(this.isSelect); } else if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { // "*/$ref" is supported in expand if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash && isSelect) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.lexer.Position)); } else if (previousSegment != null && !isSelect) { // expand option like "customer?$expand=VIPCUstomer/*" is not allowed as specification does not allowed any property before *. throw new ODataException(ODataErrorStrings.ExpressionToken_NoSegmentAllowedBeforeStarInExpand); } propertyName = this.lexer.CurrentToken.Text; this.lexer.NextToken(); } else { propertyName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); } return(new NonSystemToken(propertyName, null, previousSegment)); }
/// <summary> /// Parses a single term in a comma seperated list of things to select. /// </summary> /// <param name="isInnerTerm">is this an inner or outer select term</param> /// <returns>A token representing thing to select.</returns> public PathSegmentToken ParseSingleSelectTerm(bool isInnerTerm) { this.isSelect = true; PathSegmentToken path = this.ParseSelectExpandProperty(); if (this.IsNotEndOfTerm(isInnerTerm)) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.Lexer.ExpressionText)); } return(path); }
public void ReversePathWorksWithDeepPath() { // $expand=1/2/3/4 PathReverser pathReverser = new PathReverser(); NonSystemToken endPath = new NonSystemToken("4", null, new NonSystemToken("3", null, new NonSystemToken("2", null, new NonSystemToken("1", null, null)))); PathSegmentToken reversedPath = endPath.Accept(pathReverser); reversedPath.ShouldBeNonSystemToken("1") .NextToken.ShouldBeNonSystemToken("2") .NextToken.ShouldBeNonSystemToken("3") .NextToken.ShouldBeNonSystemToken("4"); }
public void InnerSelectSetCorrectly() { // Arrange & Act SelectToken select = new SelectToken(new PathSegmentToken[] { new NonSystemToken("abc", null, null) }); SelectTermToken selectTerm = new SelectTermToken(new NonSystemToken("stuff", null, null), select); // Assert Assert.NotNull(selectTerm.SelectOption); PathSegmentToken token = Assert.Single(selectTerm.SelectOption.Properties); NonSystemToken nonSystemToken = Assert.IsType <NonSystemToken>(token); Assert.Equal("abc", nonSystemToken.Identifier); }
public void ReversePathWorksWithSingleSegment() { // Arrange: $expand=1 PathSegmentToken nonReversedPath = new NonSystemToken("1", null, null); // Act PathSegmentToken reversedPath = nonReversedPath.Reverse(); // Assert NonSystemToken nonSystemToken = reversedPath.ShouldBeNonSystemToken("1"); Assert.Null(nonSystemToken.NextToken); }
/// <summary> /// Translate a NonSystemToken. /// </summary> /// <param name="tokenIn">The NonSystemToken to translate.</param> public void Visit(NonSystemToken tokenIn) { if (tokenIn.Identifier != UriHelper.ASTERISK.ToString()) { if (tokenIn.NextToken == null) { return; } previous = tokenIn; tokenIn.NextToken.Accept(this); } else { previous.SetNextToken(null); return; } }
/// <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="resolver">Resolver for uri parser.</param> /// <param name="currentLevelType">the top level 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, ODataUriResolver resolver, ref IEdmStructuredType currentLevelType, 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) { IEdmType previousLevelEntityType = currentLevelType; currentLevelType = UriEdmHelpers.FindTypeFromModel(model, currentToken.Identifier, resolver) as IEdmStructuredType; if (currentLevelType == null) { // TODO: fix this error message? throw new ODataException(ODataErrorStrings.ExpandItemBinder_CannotFindType(currentToken.Identifier)); } UriEdmHelpers.CheckRelatedTo(previousLevelEntityType, currentLevelType); pathToReturn.Add(new TypeSegment(currentLevelType, /*entitySet*/null)); index++; currentToken = currentToken.NextToken; if (index >= maxDepth) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_PathTooDeep); } } firstNonTypeToken = currentToken; return pathToReturn; }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the type of the current scope based on type segments.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver = null) { if (resolver == null) { resolver = ODataUriResolver.Default; } ODataPathSegment nextSegment; if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return nextSegment; } // 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 (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return nextSegment; } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return null; } } if (edmType.IsOpen) { return new OpenPropertySegment(tokenIn.Identifier); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.ODataFullName(), tokenIn.Identifier)); }
/// <summary> /// Building off of a PathSegmentToken whose value is star, only nested level options is allowed. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <returns>An expand term token based on the path token, and all available expand options.</returns> private List<ExpandTermToken> BuildStarExpandTermToken(PathSegmentToken pathToken) { List<ExpandTermToken> expandTermTokenList = new List<ExpandTermToken>(); long? levelsOption = null; bool isRefExpand = (pathToken.Identifier == UriQueryConstants.RefSegment); // Based on the specification, // For star in expand, this will be supported, // $expand=* // $expand=EntitySet($expand=* ) // $expand=*/$ref // $expand=*,EntitySet // $expand=EntitySet, * // $expand=*/$ref,EntitySet // Parenthesized set of expand options for star expand option supported are $level per specification. // And this will throw exception, // $expand= * /$count // Parenthesized set of expand options for star expand option which will also cause exception are $filter, $select, $orderby, $skip, $top, $count, $search, and $expand per specification. // And level is not supported with "*/$ref". // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // advance past the '(' this.lexer.NextToken(); // Check for (), which is not allowed. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier)); } // Only level option is supported by expand. while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionLevels: { if (!isRefExpand) { levelsOption = ResolveLevelOption(); } else { // no option is allowed when expand with star per specification throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStarRef(this.lexer.ExpressionText)); } break; } default: { throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStar(this.lexer.ExpressionText)); } } } // Move past the ')' this.lexer.NextToken(); } // Either there was no '(' at all or we just read past the ')' so we should be at the end if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. var entityType = parentEntityType as IEdmEntityType; if (entityType == null) { throw new ODataException(ODataErrorStrings.UriExpandParser_ParentEntityIsNull(this.lexer.ExpressionText)); } foreach (var navigationProperty in entityType.NavigationProperties()) { var tmpPathToken = default(PathSegmentToken); // create path token for each navigation properties. if (pathToken.Identifier.Equals(UriQueryConstants.RefSegment)) { tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken.NextToken); tmpPathToken = new NonSystemToken(UriQueryConstants.RefSegment, null, tmpPathToken); } else { tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken); } ExpandTermToken currentToken = new ExpandTermToken(tmpPathToken, null, null, null, null, null, levelsOption, null, null, null); expandTermTokenList.Add(currentToken); } return expandTermTokenList; }
/// <summary> /// Building off of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc) /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param> /// <returns>The list of expand term tokens based on the path token, and all available expand options.</returns> internal List<ExpandTermToken> BuildExpandTermToken(PathSegmentToken pathToken, string optionsText) { // Setup a new lexer for parsing the optionsText this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/); // $expand option with star only support $ref option, $expand option property could be "*" or "*/$ref", special logic will be adopted. if (pathToken.Identifier == UriQueryConstants.Star || (pathToken.Identifier == UriQueryConstants.RefSegment && pathToken.NextToken.Identifier == UriQueryConstants.Star)) { return BuildStarExpandTermToken(pathToken); } QueryToken filterOption = null; IEnumerable<OrderByToken> orderByOptions = null; long? topOption = null; long? skipOption = null; bool? countOption = null; long? levelsOption = null; QueryToken searchOption = null; SelectToken selectOption = null; ExpandToken expandOption = null; if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // advance past the '(' this.lexer.NextToken(); // Check for (), which is not allowed. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier)); } // Look for all the supported query options while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionFilter: { // advance to the equal sign this.lexer.NextToken(); string filterText = this.ReadQueryOption(); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); filterOption = filterParser.ParseFilter(filterText); break; } case ExpressionConstants.QueryOptionOrderby: { // advance to the equal sign this.lexer.NextToken(); string orderByText = this.ReadQueryOption(); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); orderByOptions = orderbyParser.ParseOrderBy(orderByText); break; } case ExpressionConstants.QueryOptionTop: { // advance to the equal sign this.lexer.NextToken(); string topText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long top; if (!long.TryParse(topText, out top) || top < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidTopOption(topText)); } topOption = top; break; } case ExpressionConstants.QueryOptionSkip: { // advance to the equal sign this.lexer.NextToken(); string skipText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long skip; if (!long.TryParse(skipText, out skip) || skip < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText)); } skipOption = skip; break; } case ExpressionConstants.QueryOptionCount: { // advance to the equal sign this.lexer.NextToken(); string countText = this.ReadQueryOption(); switch (countText) { case ExpressionConstants.KeywordTrue: { countOption = true; break; } case ExpressionConstants.KeywordFalse: { countOption = false; break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText)); } } break; } case ExpressionConstants.QueryOptionLevels: { levelsOption = ResolveLevelOption(); break; } case ExpressionConstants.QueryOptionSearch: { // advance to the equal sign this.lexer.NextToken(); string searchText = this.ReadQueryOption(); SearchParser searchParser = new SearchParser(this.MaxSearchDepth); searchOption = searchParser.ParseSearch(searchText); break; } case ExpressionConstants.QueryOptionSelect: { // advance to the equal sign this.lexer.NextToken(); string selectText = this.ReadQueryOption(); SelectExpandParser innerSelectParser = new SelectExpandParser(selectText, this.maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier); selectOption = innerSelectParser.ParseSelect(); break; } case ExpressionConstants.QueryOptionExpand: { // advance to the equal sign this.lexer.NextToken(); string expandText = this.ReadQueryOption(); // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. IEdmStructuredType targetEntityType = null; if (this.resolver != null && this.parentEntityType != null) { var parentProperty = this.resolver.ResolveProperty(parentEntityType, pathToken.Identifier) as IEdmNavigationProperty; // it is a navigation property, need to find the type. Like $expand=Friends($expand=Trips($expand=*)), when expandText becomes "Trips($expand=*)", find navigation property Trips of Friends, then get Entity type of Trips. if (parentProperty != null) { targetEntityType = parentProperty.ToEntityType(); } } SelectExpandParser innerExpandParser = new SelectExpandParser(resolver, expandText, targetEntityType, this.maxRecursionDepth - 1, enableCaseInsensitiveBuiltinIdentifier); expandOption = innerExpandParser.ParseExpand(); break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } } } // Move past the ')' this.lexer.NextToken(); } // Either there was no '(' at all or we just read past the ')' so we should be at the end if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } // TODO, there should be some check here in case pathToken identifier is $ref, select, expand and levels options are not allowed. List<ExpandTermToken> expandTermTokenList = new List<ExpandTermToken>(); ExpandTermToken currentToken = new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption, skipOption, countOption, levelsOption, searchOption, selectOption, expandOption); expandTermTokenList.Add(currentToken); return expandTermTokenList; }
/// <summary> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousSegment">Previously parsed PathSegmentToken, or null if this is the first token.</param> /// <param name="allowRef">Whether the $ref operation is valid in this token.</param> /// <returns>A parsed PathSegmentToken representing the next segment in this path.</returns> private PathSegmentToken ParseSegment(PathSegmentToken previousSegment, bool allowRef) { // TODO $count is defined in specification for expand, it is not supported now. Also note $count is not supported with star as expand option. if (this.lexer.CurrentToken.Text.StartsWith("$", StringComparison.Ordinal) && (!allowRef || this.lexer.CurrentToken.Text != UriQueryConstants.RefSegment)) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.lexer.CurrentToken.Text, this.lexer.ExpressionText)); } // Some check here to throw exception, both prop1/*/prop2 and */$ref/prop will throw exception, both are for $expand cases if (!isSelect) { if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.Star && this.lexer.CurrentToken.GetIdentifier() != UriQueryConstants.RefSegment) { // Star can only be followed with $ref throw new ODataException(ODataErrorStrings.ExpressionToken_OnlyRefAllowWithStarInExpand); } else if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.RefSegment) { // $ref should not have more property followed. throw new ODataException(ODataErrorStrings.ExpressionToken_NoPropAllowedAfterRef); } } string propertyName; if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.lexer.ReadDottedIdentifier(this.isSelect); } else if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { // "*/$ref" is supported in expand if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash && isSelect) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.lexer.Position)); } else if (previousSegment != null && !isSelect) { // expand option like "customer?$expand=VIPCUstomer/*" is not allowed as specification does not allowed any property before *. throw new ODataException(ODataErrorStrings.ExpressionToken_NoSegmentAllowedBeforeStarInExpand); } propertyName = this.lexer.CurrentToken.Text; this.lexer.NextToken(); } else { propertyName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); } return new NonSystemToken(propertyName, null, previousSegment); }
private static void VerifyPathSegmentTokensAreEqual(PathSegmentToken expected, PathSegmentToken actual, AssertionHandler assert) { try { if (!VerifyNullnessMatches(expected, actual, assert, "token")) return; assert.AreEqual(expected.GetType(), actual.GetType(), "The token kinds are different."); assert.AreEqual(expected.Identifier, actual.Identifier, "The token identifiers are different."); VerifyPathSegmentTokensAreEqual(expected.NextToken, actual.NextToken, assert); } catch (Exception) { assert.Warn("Expected query token: " + expected.ToDebugString()); assert.Warn("Actual query token: " + actual.ToDebugString()); throw; } }
/// <summary> /// Building off of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc) /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param> /// <returns>An expand term token based on the path token, and all available expand options.</returns> internal ExpandTermToken BuildExpandTermToken(PathSegmentToken pathToken, string optionsText) { // Setup a new lexer for parsing the optionsText this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/); QueryToken filterOption = null; IEnumerable<OrderByToken> orderByOptions = null; long? topOption = null; long? skipOption = null; bool? countOption = null; long? levelsOption = null; QueryToken searchOption = null; SelectToken selectOption = null; ExpandToken expandOption = null; if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // advance past the '(' this.lexer.NextToken(); // Check for (), which is not allowed. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier)); } // Look for all the supported query options while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionFilter: { // advance to the equal sign this.lexer.NextToken(); string filterText = this.ReadQueryOption(); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); filterOption = filterParser.ParseFilter(filterText); break; } case ExpressionConstants.QueryOptionOrderby: { // advance to the equal sign this.lexer.NextToken(); string orderByText = this.ReadQueryOption(); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); orderByOptions = orderbyParser.ParseOrderBy(orderByText); break; } case ExpressionConstants.QueryOptionTop: { // advance to the equal sign this.lexer.NextToken(); string topText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long top; if (!long.TryParse(topText, out top) || top < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidTopOption(topText)); } topOption = top; break; } case ExpressionConstants.QueryOptionSkip: { // advance to the equal sign this.lexer.NextToken(); string skipText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long skip; if (!long.TryParse(skipText, out skip) || skip < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText)); } skipOption = skip; break; } case ExpressionConstants.QueryOptionCount: { // advance to the equal sign this.lexer.NextToken(); string countText = this.ReadQueryOption(); switch (countText) { case ExpressionConstants.KeywordTrue: { countOption = true; break; } case ExpressionConstants.KeywordFalse: { countOption = false; break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText)); } } break; } case ExpressionConstants.QueryOptionLevels: { // advance to the equal sign this.lexer.NextToken(); string levelsText = this.ReadQueryOption(); long level; if (string.Equals( ExpressionConstants.KeywordMax, levelsText, this.enableCaseInsensitiveBuiltinIdentifier ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) { levelsOption = long.MinValue; } else if (!long.TryParse(levelsText, out level) || level < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidLevelsOption(levelsText)); } else { levelsOption = level; } break; } case ExpressionConstants.QueryOptionSearch: { // advance to the equal sign this.lexer.NextToken(); string searchText = this.ReadQueryOption(); SearchParser searchParser = new SearchParser(this.MaxSearchDepth); searchOption = searchParser.ParseSearch(searchText); break; } case ExpressionConstants.QueryOptionSelect: { // advance to the equal sign this.lexer.NextToken(); string selectText = this.ReadQueryOption(); SelectExpandParser innerSelectParser = new SelectExpandParser(selectText, this.maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier); selectOption = innerSelectParser.ParseSelect(); break; } case ExpressionConstants.QueryOptionExpand: { // advance to the equal sign this.lexer.NextToken(); string expandText = this.ReadQueryOption(); SelectExpandParser innerExpandParser = new SelectExpandParser(expandText, this.maxRecursionDepth - 1, enableCaseInsensitiveBuiltinIdentifier); expandOption = innerExpandParser.ParseExpand(); break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } } } // Move past the ')' this.lexer.NextToken(); } // Either there was no '(' at all or we just read past the ')' so we should be at the end if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } return new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption, skipOption, countOption, levelsOption, searchOption, selectOption, expandOption); }
/// <summary> /// Create a new AddNewEndingTokenVisitor, with the new token to add at the end. /// </summary> /// <param name="newTokenToAdd">a new token to add at the end of the path, can be null</param> public AddNewEndingTokenVisitor(PathSegmentToken newTokenToAdd) { this.newTokenToAdd = newTokenToAdd; }
internal static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel model, IEdmStructuredType entityType, out ODataPathSegment segment) { Debug.Assert(pathToken != null, "pathToken != null"); Debug.Assert(entityType != null, "bindingType != null"); List<IEdmOperation> possibleFunctions = new List<IEdmOperation>(); // Catch all catchable exceptions as FindDeclaredBoundOperations is implemented by anyone. // If an exception occurs it will be supressed and the possible functions will be empty and return false. try { int wildCardPos = pathToken.Identifier.IndexOf("*", StringComparison.Ordinal); if (wildCardPos > -1) { string namespaceName = pathToken.Identifier.Substring(0, wildCardPos - 1); possibleFunctions = model.FindBoundOperations(entityType).Where(o => o.Namespace == namespaceName).ToList(); } else { NonSystemToken nonSystemToken = pathToken as NonSystemToken; IList<string> parameterNames = new List<string>(); if (nonSystemToken != null && nonSystemToken.NamedValues != null) { parameterNames = nonSystemToken.NamedValues.Select(s => s.Name).ToList(); } if (parameterNames.Count > 0) { // Always force to use fully qualified name when select operation possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).FilterOperationsByParameterNames(parameterNames, false).ToList(); } else { possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).ToList(); } } } catch (Exception exc) { if (!ExceptionUtils.IsCatchableExceptionType(exc)) { throw; } } possibleFunctions = possibleFunctions.EnsureOperationsBoundWithBindingParameter().ToList(); // Only filter if there is more than one and its needed. if (possibleFunctions.Count > 1) { possibleFunctions = possibleFunctions.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(entityType).ToList(); } if (possibleFunctions.Count <= 0) { segment = null; return false; } segment = new OperationSegment(possibleFunctions, null /*entitySet*/); return true; }
private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmStructuredType edmType, ODataUriResolver resolver, out ODataPathSegment segment) { IEdmProperty prop = resolver.ResolveProperty(edmType, 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> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousSegment">Previously parsed PathSegmentToken, or null if this is the first token.</param> /// <returns>A parsed PathSegmentToken representing the next segment in this path.</returns> private PathSegmentToken ParseSegment(PathSegmentToken previousSegment) { if (this.lexer.CurrentToken.Text.StartsWith("$", StringComparison.CurrentCulture)) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.lexer.CurrentToken.Text, this.lexer.ExpressionText)); } string propertyName; if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.lexer.ReadDottedIdentifier(this.isSelect); } else if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.lexer.Position)); } propertyName = this.lexer.CurrentToken.Text; this.lexer.NextToken(); } else { propertyName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); } return new NonSystemToken(propertyName, null, previousSegment); }