/// <summary> /// Bind this function call token as a Uri function /// </summary> /// <param name="functionCallToken">the function call token to bind</param> /// <param name="argumentNodes">list of semantically bound arguments</param> /// <returns>A function call node bound to this function.</returns> private QueryNode BindAsUriFunction(FunctionCallToken functionCallToken, List <QueryNode> argumentNodes) { if (functionCallToken.Source != null) { // the parent must be null for a Uri function. throw new ODataException(ODataErrorStrings.FunctionCallBinder_UriFunctionMustHaveHaveNullParent(functionCallToken.Name)); } string functionCallTokenName = this.state.Configuration.EnableCaseInsensitiveUriFunctionIdentifier ? functionCallToken.Name.ToLowerInvariant() : functionCallToken.Name; // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a function signature, // for these, we just Bind them directly to a SingleValueFunctionCallNode if (IsUnboundFunction(functionCallTokenName)) { return(CreateUnboundFunctionNode(functionCallTokenName, argumentNodes)); } // Do some validation and get potential Uri functions that could match what we saw FunctionSignatureWithReturnType[] signatures = GetUriFunctionSignatures(functionCallTokenName); SingleValueNode[] argumentNodeArray = ValidateArgumentsAreSingleValue(functionCallTokenName, argumentNodes); FunctionSignatureWithReturnType signature = MatchSignatureToUriFunction(functionCallTokenName, argumentNodeArray, signatures); if (signature.ReturnType != null) { TypePromoteArguments(signature, argumentNodes); } if (signature.ReturnType != null && signature.ReturnType.IsStructured()) { return(new SingleResourceFunctionCallNode(functionCallTokenName, new ReadOnlyCollection <QueryNode>(argumentNodes), signature.ReturnType.AsStructured(), null)); } return(new SingleValueFunctionCallNode(functionCallTokenName, new ReadOnlyCollection <QueryNode>(argumentNodes), signature.ReturnType)); }
/// <summary> /// Try to parse an identifier that represents a function. If the parser instance has /// <see cref="restoreStateIfFail"/> set as false, then an <see cref="ODataException"/> /// is thrown if the parser finds an error. /// </summary> /// <param name="parent">Token for the parent of the function being parsed.</param> /// <param name="result">QueryToken representing this function.</param> /// <returns>True if the parsing was successful.</returns> public bool TryParseIdentifierAsFunction(QueryToken parent, out QueryToken result) { result = null; string functionName; ExpressionLexer.ExpressionLexerPosition position = lexer.SnapshotPosition(); if (this.Lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { // handle the case where we prefix a function with its namespace. functionName = this.Lexer.ReadDottedIdentifier(false); } else { Debug.Assert(this.Lexer.CurrentToken.Kind == ExpressionTokenKind.Identifier, "Only identifier tokens can be treated as function calls."); functionName = this.Lexer.CurrentToken.Text; this.Lexer.NextToken(); } FunctionParameterToken[] arguments = this.ParseArgumentListOrEntityKeyList(() => lexer.RestorePosition(position)); if (arguments != null) { result = new FunctionCallToken(functionName, arguments, parent); } return(result != null); }
/// <summary> /// Binds the token to a SingleValueFunctionCallNode or a SingleResourceFunctionCallNode for complex /// </summary> /// <param name="functionCallToken">Token to bind</param> /// <returns>The resulting SingleValueFunctionCallNode/SingleResourceFunctionCallNode</returns> internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken) { ExceptionUtils.CheckArgumentNotNull(functionCallToken, "functionCallToken"); ExceptionUtils.CheckArgumentNotNull(functionCallToken.Name, "functionCallToken.Name"); // Bind the parent, if present. // TODO: parent can be a collection as well, so we need to loosen this to QueryNode. QueryNode parent = null; if (state.ImplicitRangeVariable != null) { if (functionCallToken.Source != null) { parent = this.bindMethod(functionCallToken.Source); } else { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); } } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. var colNode = parent as CollectionNavigationNode; if (colNode != null && functionCallToken.Name == UriQueryConstants.CountSegment) { FilterClause filterOption = null; var filter = functionCallToken.Arguments.FirstOrDefault(a => a.ParameterName == "$filter"); if (filter != null) { MetadataBinder binder = this.BuildNewMetadataBinder(colNode.NavigationSource); FilterBinder filterBinder = new FilterBinder(binder.Bind, binder.BindingState); filterOption = filterBinder.BindFilter(filter.ValueToken); } // create a collection count node for collection node property. return(new CountNode(colNode, filterOption)); } // First see if there is a custom function for this QueryNode boundFunction; if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, parent, state, out boundFunction)) { return(boundFunction); } // then check if there is a global custom function(i.e with out a parent node) if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, null, state, out boundFunction)) { return(boundFunction); } // If there isn't, bind as Uri function // Bind all arguments List <QueryNode> argumentNodes = new List <QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar))); return(BindAsUriFunction(functionCallToken, argumentNodes)); }
/// <summary> /// Binds the token to a SingleValueFunctionCallNode or a SingleResourceFunctionCallNode for complex /// </summary> /// <param name="functionCallToken">Token to bind</param> /// <returns>The resulting SingleValueFunctionCallNode/SingleResourceFunctionCallNode</returns> internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken) { ExceptionUtils.CheckArgumentNotNull(functionCallToken, "functionCallToken"); ExceptionUtils.CheckArgumentNotNull(functionCallToken.Name, "functionCallToken.Name"); // Bind the parent, if present. // TODO: parent can be a collection as well, so we need to loosen this to QueryNode. QueryNode parent = null; if (state.ImplicitRangeVariable != null) { if (functionCallToken.Source != null) { parent = this.bindMethod(functionCallToken.Source); } else { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); } } // First see if there is a custom function for this QueryNode boundFunction; if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, parent, state, out boundFunction)) { return(boundFunction); } // then check if there is a global custom function(i.e with out a parent node) if (this.TryBindIdentifier(functionCallToken.Name, functionCallToken.Arguments, null, state, out boundFunction)) { return(boundFunction); } // If there isn't, bind as Uri function // Bind all arguments List <QueryNode> argumentNodes = new List <QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar))); return(BindAsUriFunction(functionCallToken, argumentNodes)); }
/// <summary> /// Binds a function call token. /// </summary> /// <param name="functionCallToken">The function call token to bind.</param> /// <returns>The bound function call token.</returns> protected virtual QueryNode BindFunctionCall(FunctionCallToken functionCallToken) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.Bind, this.BindingState); return(functionCallBinder.BindFunctionCall(functionCallToken)); }
/// <summary> /// Visits a FunctionCallToken /// </summary> /// <param name="tokenIn">The FunctionCallToken to visit</param> /// <returns>A SingleValueFunctionCallNode bound to this FunctionCallToken</returns> public virtual T Visit(FunctionCallToken tokenIn) { throw new NotImplementedException(); }