/// <summary> /// Binds a an end path token into a PropertyAccessToken, OpenPropertyToken, or FunctionCallToken. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <returns>A Query node representing this endpath token, bound to metadata.</returns> internal QueryNode BindEndPath(EndPathToken endPathToken) { ExceptionUtils.CheckArgumentNotNull(endPathToken, "EndPathToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(endPathToken.Identifier, "EndPathToken.Identifier"); // Set the parent (get the parent type, so you can check whether the Identifier inside EndPathToken really is legit offshoot of the parent type) QueryNode parent = this.DetermineParentNode(endPathToken); QueryNode boundFunction; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent == null) { if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. CollectionNode colNode = parent as CollectionNode; if (colNode != null && endPathToken.Identifier.Equals(UriQueryConstants.CountSegment)) { // create a collection count node for collection node property. return(new CountNode(colNode)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier)); } // Now that we have the parent type, can find its corresponding EDM type IEdmStructuredTypeReference structuredParentType = singleValueParent.TypeReference == null ? null : singleValueParent.TypeReference.AsStructuredOrNull(); IEdmProperty property = structuredParentType == null ? null : this.Resolver.ResolveProperty(structuredParentType.StructuredDefinition(), endPathToken.Identifier); if (property != null) { return(GeneratePropertyAccessQueryNode(singleValueParent as SingleResourceNode, property, state)); } if (endPathToken.Identifier == ExpressionConstants.QueryOptionCount) { return(new CountVirtualPropertyNode()); } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, singleValueParent, state, out boundFunction)) { return(boundFunction); } return(GeneratePropertyAccessQueryForOpenType(endPathToken, singleValueParent)); }
/// <summary> /// Determines the parent node. If the token has a parent, that token is bound. If not, then we /// use the implicit parameter from the BindingState as the parent node. /// </summary> /// <param name="segmentToken">Token to determine the parent node for.</param> /// <returns>A SingleValueQueryNode that is the parent node of the <paramref name="segmentToken"/>.</returns> private QueryNode DetermineParentNode(EndPathToken segmentToken) { ExceptionUtils.CheckArgumentNotNull(segmentToken, "segmentToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); if (segmentToken.NextToken != null) { return(this.bindMethod(segmentToken.NextToken)); } else { RangeVariable implicitRangeVariable = state.ImplicitRangeVariable; return(NodeFactory.CreateRangeVariableReferenceNode(implicitRangeVariable)); } }
internal AggregateTokenBase ParseAggregateExpression() { try { this.parseAggregateExpresionDepth++; // expression QueryToken expression = ParseLogicalOr(); if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // When there's a parenthesis after the expression we have a entity set aggregation. // The syntax is the same as the aggregate expression itself, so we recurse on ParseAggregateExpressions. this.aggregateExpressionParents.Push(expression); List <AggregateTokenBase> statements = ParseAggregateExpressions(); this.aggregateExpressionParents.Pop(); return(new EntitySetAggregateToken(expression, statements)); } AggregationMethodDefinition verb; // "with" verb EndPathToken endPathExpression = expression as EndPathToken; if (endPathExpression != null && endPathExpression.Identifier == ExpressionConstants.QueryOptionCount) { // e.g. aggregate($count as Count) verb = AggregationMethodDefinition.VirtualPropertyCount; } else { // e.g. aggregate(UnitPrice with sum as Total) verb = this.ParseAggregateWith(); } // "as" alias StringLiteralToken alias = this.ParseAggregateAs(); return(new AggregateExpressionToken(expression, verb, alias.Text)); } finally { this.parseAggregateExpresionDepth--; } }
/// <summary> /// Try to bind an end path token as a function call. Used for bound functions without parameters /// that parse as end path tokens syntactically /// </summary> /// <param name="endPathToken">the end path token to bind</param> /// <param name="parent">the parent node to this end path token.</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundFunction">a single value function call node representing the function call, if it exists</param> /// <returns>true if we found a function for this token, false otherwise.</returns> internal bool TryBindEndPathAsFunctionCall(EndPathToken endPathToken, QueryNode parent, BindingState state, out QueryNode boundFunction) { return(this.TryBindIdentifier(endPathToken.Identifier, null, parent, state, out boundFunction)); }
/// <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> /// <param name="state">The binding state.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver, BindingState state = null) { ExceptionUtils.CheckArgumentNotNull(resolver, "resolver"); ODataPathSegment nextSegment; if (UriParserHelper.IsAnnotation(tokenIn.Identifier)) { if (TryBindAsDeclaredTerm(tokenIn, model, resolver, out nextSegment)) { return(nextSegment); } string qualifiedTermName = tokenIn.Identifier.Remove(0, 1); int separator = qualifiedTermName.LastIndexOf(".", StringComparison.Ordinal); string namespaceName = qualifiedTermName.Substring(0, separator); string termName = qualifiedTermName.Substring(separator == 0 ? 0 : separator + 1); // Don't allow selecting odata control information if (String.Compare(namespaceName, ODataConstants.ODataPrefix, StringComparison.OrdinalIgnoreCase) == 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(tokenIn.Identifier)); } return(new AnnotationSegment(new EdmTerm(namespaceName, termName, EdmCoreModel.Instance.GetUntyped()))); } EndPathToken endPathToken = new EndPathToken(tokenIn.Identifier, null); if ((state?.IsCollapsed ?? false) && !(state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(tokenIn.Identifier)); } if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return(nextSegment); } // Operations must be container-qualified, and because the token type indicates it was not a .-separated 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 || (state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { return(new DynamicPathSegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.FullTypeName(), tokenIn.Identifier)); }
/// <summary> /// This method generates a <see cref="SingleValueOpenPropertyAccessNode"/> for properties of open type /// </summary> /// <param name="endPathToken">EndPathToken to bind into an open property node.</param> /// <param name="parentNode">Parent node of this open property</param> /// <returns>Will return a <see cref="SingleValueOpenPropertyAccessNode"/> when open types are supported</returns> internal SingleValueOpenPropertyAccessNode GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode) { if (parentNode.TypeReference == null || parentNode.TypeReference.Definition.IsOpen() || IsAggregatedProperty(endPathToken.Identifier)) { return(new SingleValueOpenPropertyAccessNode(parentNode, endPathToken.Identifier)); } else { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared( parentNode.TypeReference.FullName(), endPathToken.Identifier)); } }
/// <summary> /// Binds a property access token. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <returns>The bound property access token.</returns> protected virtual QueryNode BindEndPath(EndPathToken endPathToken) { EndPathBinder endPathBinder = new EndPathBinder(this.Bind, this.BindingState); return(endPathBinder.BindEndPath(endPathToken)); }
/// <summary> /// Determines the token if represents an aggregated property or not. /// </summary> /// <param name="endPath">EndPathToken to check in the list of aggregated properties.</param> /// <returns>Whether the token represents an aggregated property.</returns> private bool IsAggregatedProperty(EndPathToken endPath) => state?.AggregatedPropertyNames?.Contains(endPath) ?? false;
/// <summary> /// Visits an EndPathToken /// </summary> /// <param name="tokenIn">The EndPathToken to bind</param> /// <returns>A PropertyAccessClause bound to this EndPathToken</returns> public virtual T Visit(EndPathToken tokenIn) { throw new NotImplementedException(); }
/// <summary> /// This method generates a <see cref="SingleValueOpenPropertyAccessNode"/> for properties of open type /// </summary> /// <param name="endPathToken">EndPathToken to bind into an open property node.</param> /// <param name="parentNode">Parent node of this open property</param> /// <returns>Will return a <see cref="SingleValueOpenPropertyAccessNode"/> when open types are supported</returns> internal SingleValueOpenPropertyAccessNode GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode) { if (parentNode.TypeReference == null || parentNode.TypeReference.Definition.IsOpen() || IsAggregatedProperty(endPathToken.Identifier)) { return(new SingleValueOpenPropertyAccessNode(parentNode, endPathToken.Identifier)); } else { throw ExceptionUtil.CreatePropertyNotFoundException(endPathToken.Identifier, parentNode.TypeReference.FullName()); } }