Esempio n. 1
0
        /// <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));
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        /// <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));
        }
Esempio n. 4
0
        /// <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));
        }
Esempio n. 5
0
        /// <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();
 }