/// <summary> /// Binds a LambdaToken to metadata. /// </summary> /// <param name="lambdaToken">Token to bind.</param> /// <param name="state">Object to hold the state of binding.</param> /// <returns>A metadata bound any or all node.</returns> internal LambdaNode BindLambdaToken(LambdaToken lambdaToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(lambdaToken, "LambdaToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); // Start by binding the parent token CollectionNode parent = this.BindParentToken(lambdaToken.Parent); RangeVariable rangeVariable = null; // Add the lambda variable to the stack if (lambdaToken.Parameter != null) { rangeVariable = NodeFactory.CreateParameterNode(lambdaToken.Parameter, parent); state.RangeVariables.Push(rangeVariable); } // Bind the expression SingleValueNode expression = this.BindExpressionToken(lambdaToken.Expression); // Create the node LambdaNode lambdaNode = NodeFactory.CreateLambdaNode(state, parent, expression, rangeVariable, lambdaToken.Kind); // Remove the lambda variable as it is now out of scope if (rangeVariable != null) { state.RangeVariables.Pop(); } return(lambdaNode); }
/// <summary> /// Creates an AnyNode or an AllNode from the given /// </summary> /// <param name="state">State of binding.</param> /// <param name="parent">Parent node to the lambda.</param> /// <param name="lambdaExpression">Bound Lambda expression.</param> /// <param name="newRangeVariable">The new range variable being added by this lambda node.</param> /// <param name="queryTokenKind">Token kind.</param> /// <returns>A new LambdaNode bound to metadata.</returns> internal static LambdaNode CreateLambdaNode( BindingState state, CollectionNode parent, SingleValueNode lambdaExpression, RangeVariable newRangeVariable, QueryTokenKind queryTokenKind) { DebugUtils.CheckNoExternalCallers(); LambdaNode lambdaNode; if (queryTokenKind == QueryTokenKind.Any) { lambdaNode = new AnyNode(new Collection <RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } else { Debug.Assert(queryTokenKind == QueryTokenKind.All, "LambdaQueryNodes must be Any or All only."); lambdaNode = new AllNode(new Collection <RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } return(lambdaNode); }
/// <summary> /// Constructs a MetadataBinder with the given <paramref name="initialState"/>. /// This constructor gets used if you are not calling the top level entry point ParseQuery. /// This is an at-your-own-risk constructor, since you must provide valid initial state. /// </summary> /// <param name="initialState">The initialState to use for binding.</param> internal MetadataBinder(BindingState initialState) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(initialState, "initialState"); ExceptionUtils.CheckArgumentNotNull(initialState.Model, "initialState.Model"); this.BindingState = initialState; }
/// <summary> /// Create a new ODataUriSemanticBinder to bind an entire uri to Metadata. /// </summary> /// <param name="bindingState">the current state of the binding algorithm</param> /// <param name="bindMethod">pointer to the metadata bind method.</param> public ODataUriSemanticBinder(BindingState bindingState, MetadataBinder.QueryTokenVisitor bindMethod) { ExceptionUtils.CheckArgumentNotNull(bindingState, "bindingState"); ExceptionUtils.CheckArgumentNotNull(bindMethod, "bindMethod"); this.bindingState = bindingState; this.bindMethod = bindMethod; }
/// <summary> /// Validate the arguments to either isof or cast /// </summary> /// <param name="state">the current state of the binding algorithm</param> /// <param name="isCast">flag to indicate which function we're validating</param> /// <param name="args">the list of arguments, which could be changed</param> /// <returns>the return type of the function.</returns> private static IEdmTypeReference ValidateIsOfOrCast(BindingState state, bool isCast, ref List <QueryNode> args) { if (args.Count != 1 && args.Count != 2) { throw new ODataErrorException( ODataErrorStrings.MetadataBinder_CastOrIsOfExpressionWithWrongNumberOfOperands(args.Count)); } ConstantNode typeArgument = args.Last() as ConstantNode; IEdmTypeReference returnType = null; if (typeArgument != null) { returnType = TryGetTypeReference(state.Model, typeArgument.Value as string); } if (returnType == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfFunctionWithoutATypeArgument); } if (returnType.IsCollection()) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfCollectionsNotSupported); } // if we only have one argument, then add the implicit range variable as the first argument. if (args.Count == 1) { args = new List <QueryNode>() { new EntityRangeVariableReferenceNode( state.ImplicitRangeVariable.Name, state.ImplicitRangeVariable as EntityRangeVariable), args[0] }; } else if (!(args[0] is SingleValueNode)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfCollectionsNotSupported); } if (isCast) { return(returnType); } else { return(EdmCoreModel.Instance.GetBoolean(true)); } }
/// <summary> /// Constructs parent node from binding state /// </summary> /// <param name="state">Current binding state</param> /// <returns>The parent node.</returns> internal static SingleValueNode CreateParentFromImplicitRangeVariable(BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(state, "state"); // If the Parent is null, then it must be referring to the implicit $it parameter if (state.ImplicitRangeVariable == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessWithoutParentParameter); } return(NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable)); }
/// <summary> /// Parses the full Uri. /// </summary> /// <param name="fullUri">The full uri to parse</param> /// <returns>An ODataUri representing the full uri</returns> private ODataUri ParseUriImplementation(Uri fullUri) { ExceptionUtils.CheckArgumentNotNull(this.configuration.Model, "model"); ExceptionUtils.CheckArgumentNotNull(this.configuration.ServiceRoot, "serviceRoot"); ExceptionUtils.CheckArgumentNotNull(fullUri, "fullUri"); SyntacticTree syntax = SyntacticTree.ParseUri(fullUri, this.configuration.ServiceRoot, this.Settings.FilterLimit); ExceptionUtils.CheckArgumentNotNull(syntax, "syntax"); BindingState state = new BindingState(this.configuration); MetadataBinder binder = new MetadataBinder(state); ODataUriSemanticBinder uriBinder = new ODataUriSemanticBinder(state, binder.Bind); return(uriBinder.BindTree(syntax)); }
/// <summary> /// Process the remaining query options (represent the set of custom query options after /// service operation parameters and system query options have been removed). /// </summary> /// <param name="bindingState">the current state of the binding algorithm.</param> /// <param name="bindMethod">pointer to a binder method.</param> /// <returns>The list of <see cref="QueryNode"/> instances after binding.</returns> public static List <QueryNode> ProcessQueryOptions(BindingState bindingState, MetadataBinder.QueryTokenVisitor bindMethod) { List <QueryNode> customQueryOptionNodes = new List <QueryNode>(); foreach (CustomQueryOptionToken queryToken in bindingState.QueryOptions) { QueryNode customQueryOptionNode = bindMethod(queryToken); if (customQueryOptionNode != null) { customQueryOptionNodes.Add(customQueryOptionNode); } } bindingState.QueryOptions = null; return(customQueryOptionNodes); }
/// <summary> /// Processes the order-by tokens of a entityCollection (if any). /// </summary> /// <param name="state">State to use for binding.</param> /// <param name="orderByTokens">The order-by tokens to bind.</param> /// <returns>An OrderByClause representing the orderby statements expressed in the tokens.</returns> internal OrderByClause BindOrderBy(BindingState state, IEnumerable <OrderByToken> orderByTokens) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(state, "state"); ExceptionUtils.CheckArgumentNotNull(orderByTokens, "orderByTokens"); OrderByClause orderByClause = null; // Go through the orderby tokens starting from the last one foreach (OrderByToken orderByToken in orderByTokens.Reverse()) { orderByClause = this.ProcessSingleOrderBy(state, orderByClause, orderByToken); } return(orderByClause); }
/// <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> /// <param name="state">Current state of binding.</param> /// <returns>A SingleValueQueryNode that is the parent node of the <paramref name="segmentToken"/>.</returns> private QueryNode DetermineParentNode(EndPathToken segmentToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(segmentToken, "segmentToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); if (segmentToken.NextToken != null) { return(this.bind(segmentToken.NextToken)); } else { RangeVariable implicitRangeVariable = state.ImplicitRangeVariable; return(NodeFactory.CreateRangeVariableReferenceNode(implicitRangeVariable)); } }
/// <summary> /// Binds a an end path token into a PropertyAccessToken, OpenPropertyToken, or FunctionCallToken. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <param name="state">State of the binding algorithm.</param> /// <returns>A Query node representing this endpath token, bound to metadata.</returns> internal QueryNode BindEndPath(EndPathToken endPathToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); 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, state); QueryNode boundFunction; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent == null) { if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } 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 : structuredParentType.FindProperty(endPathToken.Identifier); if (property != null) { return(GeneratePropertyAccessQueryNode(singleValueParent, property)); } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, singleValueParent, state, out boundFunction)) { return(boundFunction); } return(GeneratePropertyAccessQueryForOpenType(endPathToken, singleValueParent)); }
/// <summary> /// Parses a <paramref name="filter"/> clause on the given <paramref name="elementType"/>, binding /// the text into semantic nodes using the provided. /// </summary> /// <param name="filter">String representation of the filter expression.</param> /// <param name="elementType">Type that the filter clause refers to.</param> /// <param name="entitySet">EntitySet that the elements beign filtered are from.</param> /// <returns>A <see cref="FilterClause"/> representing the metadata bound filter expression.</returns> private FilterClause ParseFilterImplementation(string filter, IEdmType elementType, IEdmEntitySet entitySet) { ExceptionUtils.CheckArgumentNotNull(this.configuration, "this.configuration"); ExceptionUtils.CheckArgumentNotNull(elementType, "elementType"); ExceptionUtils.CheckArgumentNotNull(filter, "filter"); // Get the syntactic representation of the filter expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(this.Settings.FilterLimit); QueryToken filterToken = expressionParser.ParseFilter(filter); // Bind it to metadata BindingState state = new BindingState(this.configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), entitySet); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); FilterBinder filterBinder = new FilterBinder(binder.Bind, state); FilterClause boundNode = filterBinder.BindFilter(filterToken); return(boundNode); }
/// <summary> /// Parses a <paramref name="orderBy "/> clause on the given <paramref name="elementType"/>, binding /// the text into semantic nodes using the provided model. /// </summary> /// <param name="orderBy">String representation of the orderby expression.</param> /// <param name="elementType">Type that the orderby clause refers to.</param> /// <param name="entitySet">EntitySet that the elements beign filtered are from.</param> /// <returns>A <see cref="OrderByClause"/> representing the metadata bound orderby expression.</returns> private OrderByClause ParseOrderByImplementation(string orderBy, IEdmType elementType, IEdmEntitySet entitySet) { ExceptionUtils.CheckArgumentNotNull(this.configuration.Model, "model"); ExceptionUtils.CheckArgumentNotNull(elementType, "elementType"); ExceptionUtils.CheckArgumentNotNull(orderBy, "orderBy"); // Get the syntactic representation of the filter expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(this.Settings.OrderByLimit); var orderByQueryTokens = expressionParser.ParseOrderBy(orderBy); // Bind it to metadata BindingState state = new BindingState(this.configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), entitySet); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); OrderByClause orderByClause = orderByBinder.BindOrderBy(state, orderByQueryTokens); return(orderByClause); }
/// <summary> /// Binds the token to a SingleValueFunctionCallNode /// </summary> /// <param name="functionCallToken">Token to bind</param> /// <param name="state">The current state of the binding algorithm</param> /// <returns>The resulting SingleValueFunctionCallNode</returns> internal QueryNode BindFunctionCall(FunctionCallToken functionCallToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); 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 (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 built-in function // Bind all arguments List <QueryNode> argumentNodes = new List <QueryNode>(functionCallToken.Arguments.Select(ar => this.bindMethod(ar))); return(BindAsBuiltInFunction(functionCallToken, state, argumentNodes)); }
/// <summary> /// Processes the specified order-by token. /// </summary> /// <param name="state">State to use for binding.</param> /// <param name="thenBy"> The next OrderBy node, or null if there is no orderby after this.</param> /// <param name="orderByToken">The order-by token to bind.</param> /// <returns>Returns the combined entityCollection including the ordering.</returns> private OrderByClause ProcessSingleOrderBy(BindingState state, OrderByClause thenBy, OrderByToken orderByToken) { ExceptionUtils.CheckArgumentNotNull(state, "state"); ExceptionUtils.CheckArgumentNotNull(orderByToken, "orderByToken"); QueryNode expressionNode = this.bindMethod(orderByToken.Expression); // TODO: shall we really restrict order-by expressions to primitive types? SingleValueNode expressionResultNode = expressionNode as SingleValueNode; if (expressionResultNode == null || (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind())) { throw new ODataException(ODataErrorStrings.MetadataBinder_OrderByExpressionNotSingleValue); } OrderByClause orderByNode = new OrderByClause( thenBy, expressionResultNode, orderByToken.Direction, state.ImplicitRangeVariable); return(orderByNode); }
/// <summary> /// Creates a FilterBinder. /// </summary> /// <param name="bindMethod">Method to use to visit the token tree and bind the tokens recursively.</param> /// <param name="state">State to use for binding.</param> internal FilterBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) { DebugUtils.CheckNoExternalCallers(); this.bindMethod = bindMethod; this.state = state; }
/// <summary> /// Try to bind an inner path token as a function call. Used for bound functions without parameters /// that parse as inner path tokens syntactically /// </summary> /// <param name="innerPathToken">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 TryBindInnerPathAsFunctionCall(InnerPathToken innerPathToken, QueryNode parent, BindingState state, out QueryNode boundFunction) { DebugUtils.CheckNoExternalCallers(); return(this.TryBindIdentifier(innerPathToken.Identifier, null, parent, state, out boundFunction)); }
/// <summary> /// Try to bind a <see cref="DottedIdentifierToken"/> as a function call. Used for container qualified functions without parameters. /// </summary> /// <param name="dottedIdentifierToken">the dotted identifier token to bind</param> /// <param name="parent">the semantically bound parent node for this dotted identifier</param> /// <param name="state">the current stat of the binding algorithm</param> /// <param name="boundFunction">a single value function call node representing the function call, if we found one.</param> /// <returns>true if we found a function for this token, false otherwise.</returns> internal bool TryBindDottedIdentifierAsFunctionCall(DottedIdentifierToken dottedIdentifierToken, SingleValueNode parent, BindingState state, out QueryNode boundFunction) { DebugUtils.CheckNoExternalCallers(); return(this.TryBindIdentifier(dottedIdentifierToken.Identifier, null, parent, state, out boundFunction)); }
/// <summary> /// Binds a DottedIdentifierToken and it's parent node (if needed). /// </summary> /// <param name="dottedIdentifierToken">Token to bind to metadata.</param> /// <param name="state">State of the Binding.</param> /// <returns>A bound node representing the cast.</returns> internal QueryNode BindDottedIdentifier(DottedIdentifierToken dottedIdentifierToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(dottedIdentifierToken, "castToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); QueryNode parent; IEdmType parentType; if (dottedIdentifierToken.NextToken == null) { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); parentType = state.ImplicitRangeVariable.TypeReference.Definition; } else { parent = this.bindMethod(dottedIdentifierToken.NextToken); parentType = parent.GetEdmType(); } SingleEntityNode parentAsSingleValue = parent as SingleEntityNode; IEdmSchemaType childType = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier); IEdmEntityType childEntityType = childType as IEdmEntityType; if (childEntityType == null) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod); QueryNode functionCallNode; if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, parentAsSingleValue, state, out functionCallNode)) { return(functionCallNode); } else { throw new ODataException(ODataErrorStrings.CastBinder_ChildTypeIsNotEntity(dottedIdentifierToken.Identifier)); } } // Check whether childType is a derived type of the type of its parent node UriEdmHelpers.CheckRelatedTo(parentType, childType); EntityCollectionNode parentAsCollection = parent as EntityCollectionNode; if (parentAsCollection != null) { return(new EntityCollectionCastNode(parentAsCollection, childEntityType)); } // parent can be null for casts on the implicit parameter; this is OK if (parent == null) { return(new SingleEntityCastNode(null, childEntityType)); } Debug.Assert(parentAsSingleValue != null, "If parent of the cast node was not collection, it should be a single value."); return(new SingleEntityCastNode(parentAsSingleValue, childEntityType)); }
/// <summary> /// Bind this function call token as a built in function /// </summary> /// <param name="functionCallToken">the function call token to bidn</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="argumentNodes">list of semantically bound arguments</param> /// <returns>A function call node bound to this function.</returns> private static QueryNode BindAsBuiltInFunction(FunctionCallToken functionCallToken, BindingState state, List <QueryNode> argumentNodes) { if (functionCallToken.Source != null) { // the parent must be null for a built in function. throw new ODataException(ODataErrorStrings.FunctionCallBinder_BuiltInFunctionMustHaveHaveNullParent(functionCallToken.Name)); } // There are some functions (IsOf and Cast for example) that don't necessarily need to be bound to a BuiltInFunctionSignature, // for these, we just Bind them directly to a SingleValueFunctionCallNode if (IsUnboundFunction(functionCallToken.Name)) { return(CreateUnboundFunctionNode(functionCallToken, argumentNodes, state)); } // Do some validation and get potential built-in functions that could match what we saw FunctionSignatureWithReturnType[] signatures = GetBuiltInFunctionSignatures(functionCallToken.Name); IEdmTypeReference[] argumentTypes = EnsureArgumentsAreSingleValue(functionCallToken.Name, argumentNodes); FunctionSignatureWithReturnType signature = MatchSignatureToBuiltInFunction(functionCallToken.Name, argumentTypes, signatures); if (signature.ReturnType != null) { TypePromoteArguments(signature, argumentNodes); } IEdmTypeReference returnType = signature.ReturnType; return(new SingleValueFunctionCallNode(functionCallToken.Name, new ReadOnlyCollection <QueryNode>(argumentNodes), returnType)); }
/// <summary> /// Binds a <see cref="InnerPathToken"/>. /// This includes more than just navigations - it includes complex property access and primitive collections. /// </summary> /// <param name="segmentToken">The segment token to bind.</param> /// <param name="state">The state of binding.</param> /// <returns>The bound node.</returns> internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod); // First we get the parent node QueryNode parent = this.DetermineParentNode(segmentToken, state); Debug.Assert(parent != null, "parent should never be null"); SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, state, out boundFunction)) { return(boundFunction); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Identifier)); } // Using the parent and name of this token, we try to get the IEdmProperty it represents IEdmProperty property = BindProperty(singleValueParent.TypeReference, segmentToken.Identifier); if (property == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, state, out boundFunction)) { return(boundFunction); } if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpenType()) { throw new ODataException( ODataErrorStrings.MetadataBinder_PropertyNotDeclared( parent.GetEdmTypeReference().ODataFullName(), segmentToken.Identifier)); } return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier)); } if (property.Type.IsODataComplexTypeKind()) { return(new SingleValuePropertyAccessNode(singleValueParent, property)); } // Note - this means nonentity collection (primitive or complex) if (property.Type.IsNonEntityCollectionType()) { return(new CollectionPropertyAccessNode(singleValueParent, property)); } IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty; if (navigationProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name)); } SingleEntityNode parentEntity = EnsureParentIsEntityForNavProp(singleValueParent); return(GetNavigationNode(navigationProperty, parentEntity, segmentToken.NamedValues, state, new KeyBinder(this.bindMethod))); }
/// <summary> /// Builds an appropriate navigation query node (collection or single) for the given property and parent node. /// </summary> /// <param name="property">Navigation property.</param> /// <param name="parent">Parent Node.</param> /// <param name="namedValues">Named values (key values) that were included in the node we are binding, if any.</param> /// <param name="state">State of binding.</param> /// <param name="keyBinder">Object to perform binding on any key values that are present.</param> /// <returns>A new CollectionNavigationNode or SingleNavigationNode to capture the navigation propety access.</returns> internal static QueryNode GetNavigationNode(IEdmNavigationProperty property, SingleEntityNode parent, IEnumerable <NamedValue> namedValues, BindingState state, KeyBinder keyBinder) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(property, "property"); ExceptionUtils.CheckArgumentNotNull(parent, "parent"); ExceptionUtils.CheckArgumentNotNull(state, "state"); ExceptionUtils.CheckArgumentNotNull(keyBinder, "keyBinder"); // Handle collection navigation property if (property.TargetMultiplicityTemporary() == EdmMultiplicity.Many) { CollectionNavigationNode collectionNavigationNode = new CollectionNavigationNode(property, parent); // Doing key lookup on the collection navigation property if (namedValues != null) { return(keyBinder.BindKeyValues(collectionNavigationNode, namedValues)); } // Otherwise it's just a normal collection of entities return(collectionNavigationNode); } Debug.Assert(namedValues == null || !namedValues.Any(), "namedValues should not exist if it isn't a colleciton"); // Otherwise it's a single navigation property return(new SingleNavigationNode(property, parent)); }
private bool TryBindIdentifier(string identifier, IEnumerable <FunctionParameterToken> arguments, QueryNode parent, BindingState state, out QueryNode boundFunction) { boundFunction = null; IEdmType bindingType = null; var singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { if (singleValueParent.TypeReference != null) { bindingType = singleValueParent.TypeReference.Definition; } } else { var collectionValueParent = parent as CollectionNode; if (collectionValueParent != null) { bindingType = collectionValueParent.CollectionType.Definition; } } if (!UriEdmHelpers.IsBindingTypeValid(bindingType)) { return(false); } IEdmFunctionImport functionImport; List <FunctionParameterToken> syntacticArguments = arguments == null ? new List <FunctionParameterToken>() : arguments.ToList(); if (!FunctionOverloadResolver.ResolveFunctionsFromList(identifier, syntacticArguments.Select(ar => ar.ParameterName).ToList(), bindingType, state.Model, out functionImport)) { return(false); } if (singleValueParent != null && singleValueParent.TypeReference == null) { // if the parent exists, but has no type information, then we're in open type land, and we // shouldn't go any farther. throw new ODataException(ODataErrorStrings.FunctionCallBinder_CallingFunctionOnOpenProperty(identifier)); } if (functionImport.IsSideEffecting) { return(false); } ICollection <FunctionParameterToken> parsedParameters; if (!FunctionParameterParser.TryParseFunctionParameters(syntacticArguments, state.Configuration, functionImport, out parsedParameters)) { return(false); } IEnumerable <QueryNode> boundArguments = parsedParameters.Select(p => this.bindMethod(p)); IEdmTypeReference returnType = functionImport.ReturnType; IEdmEntitySet returnSet = null; var singleEntityNode = parent as SingleEntityNode; if (singleEntityNode != null) { returnSet = functionImport.GetTargetEntitySet(singleEntityNode.EntitySet, state.Model); } if (returnType.IsEntity()) { boundFunction = new SingleEntityFunctionCallNode(identifier, new[] { functionImport }, boundArguments, (IEdmEntityTypeReference)returnType.Definition.ToTypeReference(), returnSet, parent); } else if (returnType.IsEntityCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new EntityCollectionFunctionCallNode(identifier, new[] { functionImport }, boundArguments, collectionTypeReference, returnSet, parent); } else if (returnType.IsCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new CollectionFunctionCallNode(identifier, new[] { functionImport }, boundArguments, collectionTypeReference, parent); } else { boundFunction = new SingleValueFunctionCallNode(identifier, new[] { functionImport }, boundArguments, returnType, parent); } return(true); }
/// <summary> /// Build a SingleValueFunctionCallNode for a function that isn't bound to a BuiltInFunction /// </summary> /// <param name="functionCallToken">original query token for this function</param> /// <param name="args">list of already bound query nodes for this function</param> /// <param name="state">The current state of the binding algorithm.</param> /// <returns>A single value function call node bound to this function.</returns> private static SingleValueNode CreateUnboundFunctionNode(FunctionCallToken functionCallToken, List <QueryNode> args, BindingState state) { // need to figure out the return type and check the correct number of arguments based on the function name IEdmTypeReference returnType = null; switch (functionCallToken.Name) { case ExpressionConstants.UnboundFunctionIsOf: { returnType = ValidateAndBuildIsOfArgs(state, ref args); break; } case ExpressionConstants.UnboundFunctionCast: { returnType = ValidateAndBuildCastArgs(state, ref args); if (returnType.IsEntity()) { IEdmEntityTypeReference returnEntityType = returnType.AsEntity(); SingleEntityNode entityNode = args.ElementAt(0) as SingleEntityNode; if (entityNode != null) { return(new SingleEntityFunctionCallNode(functionCallToken.Name, args, returnEntityType, entityNode.EntitySet)); } } break; } default: { break; } } // we have everything else we need, so return the new SingleValueFunctionCallNode. return(new SingleValueFunctionCallNode(functionCallToken.Name, args, returnType)); }
/// <summary> /// Validate the arguments (adding the implicit range variable if necessary), and determine the correct return type /// for an IsOf function /// </summary> /// <param name="state">the current state of the binding algorithm, used to get the implicit range variable if necessary</param> /// <param name="args">current list of args, can be changed</param> /// <returns>the correct return type for this function.</returns> private static IEdmTypeReference ValidateAndBuildIsOfArgs(BindingState state, ref List <QueryNode> args) { return(ValidateIsOfOrCast(state, false, ref args)); }
/// <summary> /// Binds a parameter token. /// </summary> /// <param name="rangeVariableToken">The parameter token to bind.</param> /// <param name="state">The state of metadata binding.</param> /// <returns>The bound query node.</returns> internal static SingleValueNode BindRangeVariableToken(RangeVariableToken rangeVariableToken, BindingState state) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(rangeVariableToken, "rangeVariableToken"); RangeVariable rangeVariable = state.RangeVariables.SingleOrDefault(p => p.Name == rangeVariableToken.Name); if (rangeVariable == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_ParameterNotInScope(rangeVariableToken.Name)); } return(NodeFactory.CreateRangeVariableReferenceNode(rangeVariable)); }