/// <summary> /// Bind a parameter alias which is inside another alias value. /// </summary> /// <param name="bindingState">The alias name which is inside another alias value.</param> /// <param name="aliasToken">The cache of alias value nodes</param> /// <returns>The semantics node tree for alias (the @p1 in "@p1=...", not alias value expression)</returns> internal ParameterAliasNode BindParameterAlias(BindingState bindingState, FunctionParameterAliasToken aliasToken) { ExceptionUtils.CheckArgumentNotNull(bindingState, "bindingState"); ExceptionUtils.CheckArgumentNotNull(aliasToken, "aliasToken"); string alias = aliasToken.Alias; ParameterAliasValueAccessor aliasValueAccessor = bindingState.Configuration.ParameterAliasValueAccessor; if (aliasValueAccessor == null) { return new ParameterAliasNode(alias, null); } // in cache? SingleValueNode aliasValueNode = null; if (!aliasValueAccessor.ParameterAliasValueNodesCached.TryGetValue(alias, out aliasValueNode)) { // has value expression? string aliasValueExpression = aliasValueAccessor.GetAliasValueExpression(alias); if (aliasValueExpression == null) { aliasValueAccessor.ParameterAliasValueNodesCached[alias] = null; } else { aliasValueNode = this.ParseAndBindParameterAliasValueExpression(bindingState, aliasValueExpression, aliasToken.ExpectedParameterType); aliasValueAccessor.ParameterAliasValueNodesCached[alias] = aliasValueNode; } } return new ParameterAliasNode(alias, aliasValueNode.GetEdmTypeReference()); }
/// <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) { 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> /// 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) { 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> /// 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) { ExceptionUtils.CheckArgumentNotNull(initialState, "initialState"); ExceptionUtils.CheckArgumentNotNull(initialState.Model, "initialState.Model"); this.BindingState = initialState; }
/// <summary> /// Constructor for binderbase. /// </summary> /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param> /// <param name="state">State of the metadata binding.</param> protected BinderBase(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) { ExceptionUtils.CheckArgumentNotNull(bindMethod, "bindMethod"); ExceptionUtils.CheckArgumentNotNull(state, "state"); this.bindMethod = bindMethod; this.state = state; }
/// <summary> /// Bind a parameter alias which is inside another alias value. /// </summary> /// <param name="bindingState">The alias name which is inside another alias value.</param> /// <param name="aliasToken">The cache of alias value nodes</param> /// <returns>The semantics node tree for alias (the @p1 in "@p1=...", not alias value expression)</returns> internal ParameterAliasNode BindParameterAlias(BindingState bindingState, FunctionParameterAliasToken aliasToken) { ExceptionUtils.CheckArgumentNotNull(bindingState, "bindingState"); ExceptionUtils.CheckArgumentNotNull(aliasToken, "aliasToken"); string alias = aliasToken.Alias; ParameterAliasValueAccessor aliasValueAccessor = bindingState.Configuration.ParameterAliasValueAccessor; if (aliasValueAccessor == null) { return(new ParameterAliasNode(alias, null)); } // in cache? SingleValueNode aliasValueNode = null; if (!aliasValueAccessor.ParameterAliasValueNodesCached.TryGetValue(alias, out aliasValueNode)) { // has value expression? string aliasValueExpression = aliasValueAccessor.GetAliasValueExpression(alias); if (aliasValueExpression == null) { aliasValueAccessor.ParameterAliasValueNodesCached[alias] = null; } else { aliasValueNode = this.ParseAndBindParameterAliasValueExpression(bindingState, aliasValueExpression); aliasValueAccessor.ParameterAliasValueNodesCached[alias] = aliasValueNode; } } return(new ParameterAliasNode(alias, aliasValueNode.GetEdmTypeReference())); }
/// <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) { 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); }
public ApplyBinderTests() { var implicitRangeVariable = new EntityRangeVariable(ExpressionConstants.It, HardCodedTestModel.GetPersonTypeReference(), HardCodedTestModel.GetPeopleSet()); this._bindingState = new BindingState(_configuration) { ImplicitRangeVariable = implicitRangeVariable }; this._bindingState.RangeVariables.Push( new BindingState(_configuration) { ImplicitRangeVariable = implicitRangeVariable }.ImplicitRangeVariable); }
/// <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; }
public void Init() { this.orderbyBinder = new OrderByBinder(FakeBindMethods.BindMethodReturningASinglePrimitive); var implicitRangeVariable = new EntityRangeVariable(ExpressionConstants.It, HardCodedTestModel.GetPersonTypeReference(), HardCodedTestModel.GetPeopleSet()); this.bindingState = new BindingState(configuration) { ImplicitRangeVariable = implicitRangeVariable }; this.bindingState.RangeVariables.Push(new BindingState(configuration) { ImplicitRangeVariable = implicitRangeVariable }.ImplicitRangeVariable); }
public void PrimitiveCollectionPropertyShouldCreateMatchingNode() { var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASingleDog, state); var token = new InnerPathToken("Nicknames", new DummyToken(), null /*namedValues*/); var result = binder.BindInnerPathSegment(token); result.ShouldBeCollectionPropertyAccessQueryNode(HardCodedTestModel.GetDogNicknamesProperty()); }
public void DeclaredPropertyOnOpenTypeShouldCreateMatchingNode() { var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASinglePainting, state); var token = new InnerPathToken("Colors", new DummyToken(), null /*namedValues*/); var result = binder.BindInnerPathSegment(token); result.ShouldBeCollectionPropertyAccessQueryNode(HardCodedTestModel.GetPaintingColorsProperty()); }
internal static List <OperationSegmentParameter> BindSegmentParameters(ODataUriParserConfiguration configuration, IEdmOperation functionOrOpertion, ICollection <FunctionParameterToken> segmentParameterTokens) { // TODO: HandleComplexOrCollectionParameterValueIfExists is temp work around for single copmlex or colleciton type, it can't handle nested complex or collection value. ICollection <FunctionParameterToken> parametersParsed = FunctionCallBinder.HandleComplexOrCollectionParameterValueIfExists(configuration.Model, functionOrOpertion, segmentParameterTokens, configuration.Resolver.EnableCaseInsensitive, configuration.EnableUriTemplateParsing); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = null; state.RangeVariables.Clear(); MetadataBinder binder = new MetadataBinder(state); List <OperationSegmentParameter> boundParameters = new List <OperationSegmentParameter>(); IDictionary <string, SingleValueNode> input = new Dictionary <string, SingleValueNode>(StringComparer.Ordinal); foreach (var paraToken in parametersParsed) { // TODO: considering another better exception if (paraToken.ValueToken is EndPathToken) { throw new ODataException(Strings.MetadataBinder_ParameterNotInScope( string.Format(CultureInfo.InvariantCulture, "{0}={1}", paraToken.ParameterName, (paraToken.ValueToken as EndPathToken).Identifier))); } SingleValueNode boundNode = (SingleValueNode)binder.Bind(paraToken.ValueToken); if (!input.ContainsKey(paraToken.ParameterName)) { input.Add(paraToken.ParameterName, boundNode); } } IDictionary <IEdmOperationParameter, SingleValueNode> result = configuration.Resolver.ResolveOperationParameters(functionOrOpertion, input); foreach (var item in result) { SingleValueNode boundNode = item.Value; // ensure node type is compatible with parameter type. var sourceTypeReference = boundNode.GetEdmTypeReference(); bool sourceIsNullOrOpenType = (sourceTypeReference == null); if (!sourceIsNullOrOpenType) { // if the node has been rewritten, no further conversion is needed. if (!TryRewriteIntegralConstantNode(ref boundNode, item.Key.Type)) { boundNode = MetadataBindingUtils.ConvertToTypeIfNeeded(boundNode, item.Key.Type); } } OperationSegmentParameter boundParamer = new OperationSegmentParameter(item.Key.Name, boundNode); boundParameters.Add(boundParamer); } return(boundParameters); }
public void OpenPropertyShouldCreateMatchingNode() { const string OpenPropertyName = "Emotions"; var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASinglePainting, state); var token = new InnerPathToken(OpenPropertyName, new DummyToken(), null /*namedValues*/); var result = binder.BindInnerPathSegment(token); result.ShouldBeSingleValueOpenPropertyAccessQueryNode(OpenPropertyName); }
public void CollectionNavigationPropertyShouldCreateMatchingNode() { var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASingleDog, state); var token = new InnerPathToken("MyPeople", new DummyToken(), null /*namedValues*/); var result = binder.BindInnerPathSegment(token); result.ShouldBeCollectionNavigationNode(HardCodedTestModel.GetDogMyPeopleNavProp()). And.NavigationSource.Should().BeSameAs(HardCodedTestModel.GetDogsSet().FindNavigationTarget(HardCodedTestModel.GetDogMyPeopleNavProp())); }
public void CreateLambdaNodeForAnyTokenShouldCreateAnyNode() { BindingState bindingState = new BindingState(configuration); EntityCollectionNode parent = new EntitySetNode(HardCodedTestModel.GetPeopleSet()); SingleValueNode expression = new ConstantNode(true); RangeVariable rangeVariable = new EntityRangeVariable("bob", HardCodedTestModel.GetPersonTypeReference(), parent); var resultNode = NodeFactory.CreateLambdaNode(bindingState, parent, expression, rangeVariable, QueryTokenKind.Any); var node = resultNode.ShouldBeAnyQueryNode().And; node.Body.Should().BeSameAs(expression); node.Source.Should().BeSameAs(parent); }
private MetadataBinder BuildNewMetadataBinder(IEdmNavigationSource targetNavigationSource) { BindingState state = new BindingState(this.configuration) { ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(targetNavigationSource.EntityType().ToTypeReference(), targetNavigationSource) }; state.RangeVariables.Push(state.ImplicitRangeVariable); return(new MetadataBinder(state)); }
/// <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) { 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> /// 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) { 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); }
public void MissingPropertyShouldThrow() { const string MissingPropertyName = "ThisPropertyDoesNotExist"; var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASingleDog, state); var token = new InnerPathToken(MissingPropertyName, new DummyToken(), null /*namedValues*/); Action bind = () => binder.BindInnerPathSegment(token); string expectedMessage = ODataErrorStrings.MetadataBinder_PropertyNotDeclared(HardCodedTestModel.GetDogType().FullTypeName(), MissingPropertyName); bind.ShouldThrow<ODataException>(expectedMessage); }
/// <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) { 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); }
/// <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) { 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> /// 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) { 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(InnerPathToken segmentToken, BindingState state) { 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)); } }
/// <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); }
internal static List <OperationSegmentParameter> BindSegmentParameters(ODataUriParserConfiguration configuration, IEdmOperation functionOrOpertion, ICollection <FunctionParameterToken> segmentParameterTokens) { // TODO: HandleComplexOrCollectionParameterValueIfExists is temp work around for single copmlex or colleciton type, it can't handle nested complex or collection value. ICollection <FunctionParameterToken> parametersParsed = FunctionCallBinder.HandleComplexOrCollectionParameterValueIfExists(configuration.Model, functionOrOpertion, segmentParameterTokens, configuration.EnableUriTemplateParsing); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = null; state.RangeVariables.Clear(); MetadataBinder binder = new MetadataBinder(state); List <OperationSegmentParameter> boundParameters = new List <OperationSegmentParameter>(); foreach (var paraToken in parametersParsed) { // TODO: considering another better exception if (paraToken.ValueToken is EndPathToken) { throw new ODataException(Strings.MetadataBinder_ParameterNotInScope( string.Format(CultureInfo.InvariantCulture, "{0}={1}", paraToken.ParameterName, (paraToken.ValueToken as EndPathToken).Identifier))); } SingleValueNode boundNode = (SingleValueNode)binder.Bind(paraToken.ValueToken); // ensure parameter name existis var functionParameter = functionOrOpertion.FindParameter(paraToken.ParameterName); if (functionParameter == null) { throw new ODataException(Strings.ODataParameterWriterCore_ParameterNameNotFoundInOperation(paraToken.ParameterName, functionOrOpertion.Name)); } // ensure node type is compatible with parameter type. var sourceTypeReference = boundNode.GetEdmTypeReference(); bool sourceIsNullOrOpenType = (sourceTypeReference == null); if (!sourceIsNullOrOpenType) { boundNode = MetadataBindingUtils.ConvertToTypeIfNeeded(boundNode, functionParameter.Type); } OperationSegmentParameter boundParamer = new OperationSegmentParameter(paraToken.ParameterName, boundNode); boundParameters.Add(boundParamer); } return(boundParameters); }
/// <summary> /// Parse expression into syntaxs token tree, and bind it into semantics node tree. /// </summary> /// <param name="bindingState">The BindingState.</param> /// <param name="aliasValueExpression">The alias value's expression text.</param> /// <returns>The semantcs node of the expression text.</returns> private SingleValueNode ParseAndBindParameterAliasValueExpression(BindingState bindingState, string aliasValueExpression) { // Get the syntactic representation of the filter expression // TODO: change Settings.FilterLimit to ParameterAliasValueLimit UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(bindingState.Configuration.Settings.FilterLimit); QueryToken aliasValueToken = expressionParser.ParseExpressionText(aliasValueExpression); // Get the semantic node, and check for SingleValueNode QueryNode aliasValueNode = this.bindMethod(aliasValueToken); SingleValueNode result = aliasValueNode as SingleValueNode; if (result == null) { // TODO: add string resource throw new ODataException("ODataErrorStrings.MetadataBinder_ParameterAliasValueExpressionNotSingleValue"); } return(result); }
/// <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) { 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> /// 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) { 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 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> /// Parse expression into syntaxs token tree, and bind it into semantics node tree. /// </summary> /// <param name="bindingState">The BindingState.</param> /// <param name="aliasValueExpression">The alias value's expression text.</param> /// <param name="parameterType">The edm type of the parameter.</param> /// <returns>The semantcs node of the expression text.</returns> private SingleValueNode ParseAndBindParameterAliasValueExpression(BindingState bindingState, string aliasValueExpression, IEdmTypeReference parameterType) { // Get the syntactic representation of the filter expression // TODO: change Settings.FilterLimit to ParameterAliasValueLimit UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(bindingState.Configuration.Settings.FilterLimit); QueryToken aliasValueToken = expressionParser.ParseExpressionText(aliasValueExpression); // Special logic to handle parameter alias token. aliasValueToken = ParseComplexOrCollectionAlias(aliasValueToken, parameterType, bindingState.Model); // Get the semantic node, and check for SingleValueNode QueryNode aliasValueNode = this.bindMethod(aliasValueToken); SingleValueNode result = aliasValueNode as SingleValueNode; if (result == null) { // TODO: add string resource throw new ODataException("ODataErrorStrings.MetadataBinder_ParameterAliasValueExpressionNotSingleValue"); } return result; }
/// <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); // The order-by expressions need to be primitive / enumeration types SingleValueNode expressionResultNode = expressionNode as SingleValueNode; if (expressionResultNode == null || (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind() && !expressionResultNode.TypeReference.IsODataEnumTypeKind() && !expressionResultNode.TypeReference.IsODataTypeDefinitionTypeKind())) { throw new ODataException(ODataErrorStrings.MetadataBinder_OrderByExpressionNotSingleValue); } OrderByClause orderByNode = new OrderByClause( thenBy, expressionResultNode, orderByToken.Direction, state.ImplicitRangeVariable); return orderByNode; }
/// <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); // The order-by expressions need to be primitive / enumeration types SingleValueNode expressionResultNode = expressionNode as SingleValueNode; if (expressionResultNode == null || (expressionResultNode.TypeReference != null && !expressionResultNode.TypeReference.IsODataPrimitiveTypeKind() && !expressionResultNode.TypeReference.IsODataEnumTypeKind())) { throw new ODataException(ODataErrorStrings.MetadataBinder_OrderByExpressionNotSingleValue); } OrderByClause orderByNode = new OrderByClause( thenBy, expressionResultNode, orderByToken.Direction, state.ImplicitRangeVariable); return(orderByNode); }
/// <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> /// 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) { ExceptionUtils.CheckArgumentNotNull(property, "property"); ExceptionUtils.CheckArgumentNotNull(parent, "parent"); ExceptionUtils.CheckArgumentNotNull(state, "state"); ExceptionUtils.CheckArgumentNotNull(keyBinder, "keyBinder"); // Handle collection navigation property if (property.TargetMultiplicity() == EdmMultiplicity.Many) { CollectionNavigationNode collectionNavigationNode = new CollectionNavigationNode(property, parent); // Doing key lookup on the collection navigation property if (namedValues != null) { return(keyBinder.BindKeyValues(collectionNavigationNode, namedValues, state.Model)); } // 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)); }
/// <summary> /// Parses an <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="configuration">The configuration used for binding.</param> /// <param name="elementType">Type that the orderby clause refers to.</param> /// <param name="navigationSource">NavigationSource that the elements are from.</param> /// <returns>An <see cref="OrderByClause"/> representing the metadata bound orderby expression.</returns> private OrderByClause ParseOrderByImplementation(string orderBy, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(configuration.Model, "model"); ExceptionUtils.CheckArgumentNotNull(elementType, "elementType"); ExceptionUtils.CheckArgumentNotNull(orderBy, "orderBy"); // Get the syntactic representation of the orderby expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.OrderByLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); var orderByQueryTokens = expressionParser.ParseOrderBy(orderBy); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); if (applyClause != null) { state.AggregatedPropertyNames = applyClause.GetLastAggregatedPropertyNames(); } MetadataBinder binder = new MetadataBinder(state); OrderByBinder orderByBinder = new OrderByBinder(binder.Bind); OrderByClause orderByClause = orderByBinder.BindOrderBy(state, orderByQueryTokens); return orderByClause; }
/// <summary> /// Parses a <paramref name="filter"/> clause on the given <paramref name="elementType"/>, binding /// the text into semantic nodes using the provided model. /// </summary> /// <param name="filter">String representation of the filter expression.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="elementType">Type that the filter clause refers to.</param> /// <param name="navigationSource">Navigation source that the elements being filtered are from.</param> /// <returns>A <see cref="FilterClause"/> representing the metadata bound filter expression.</returns> private FilterClause ParseFilterImplementation(string filter, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(elementType, "elementType"); ExceptionUtils.CheckArgumentNotNull(filter, "filter"); // Get the syntactic representation of the filter expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); QueryToken filterToken = expressionParser.ParseFilter(filter); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); if (applyClause != null) { state.AggregatedPropertyNames = applyClause.GetLastAggregatedPropertyNames(); } MetadataBinder binder = new MetadataBinder(state); FilterBinder filterBinder = new FilterBinder(binder.Bind, state); FilterClause boundNode = filterBinder.BindFilter(filterToken); return boundNode; }
/// <summary> /// Constructs a EndPathBinder object using the given function to bind parent token. /// </summary> /// <param name="bindMethod">Method to bind the EndPathToken's parent, if there is one.</param> /// <param name="state">State of the metadata binding.</param>am> /// <remarks> /// Make bindMethod of return type SingleValueQueryNode. /// </remarks> internal EndPathBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) : base(bindMethod, state) { this.functionCallBinder = new FunctionCallBinder(bindMethod, state); }
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); } // All functions should be fully qualified, if they aren't they they aren't functions. // When using extension, there may be function call with unqualified name. So loose the restriction here. if (identifier.IndexOf(".", StringComparison.Ordinal) == -1 && this.Resolver.GetType() == typeof(ODataUriResolver)) { return(false); } IEdmOperation operation; List <FunctionParameterToken> syntacticArguments = arguments == null ? new List <FunctionParameterToken>() : arguments.ToList(); if (!FunctionOverloadResolver.ResolveOperationFromList(identifier, syntacticArguments.Select(ar => ar.ParameterName).ToList(), bindingType, state.Model, out operation, this.Resolver)) { // TODO: FunctionOverloadResolver.ResolveOperationFromList() looks up the function by parameter names, but it shouldn't ignore parameter types. (test case ParseFilter_AliasInFunction_PropertyAsValue_TypeMismatch should fail) 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 (operation.IsAction()) { return(false); } IEdmFunction function = (IEdmFunction)operation; // TODO: $filter $orderby parameter expression which contains complex or collection should NOT be supported in this way // but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p)); ICollection <FunctionParameterToken> parsedParameters = HandleComplexOrCollectionParameterValueIfExists(state.Configuration.Model, function, syntacticArguments, state.Configuration.Resolver.EnableCaseInsensitive); IEnumerable <QueryNode> boundArguments = parsedParameters.Select(p => this.bindMethod(p)); boundArguments = boundArguments.ToList(); // force enumerable to run : will immediately evaluate all this.bindMethod(p). IEdmTypeReference returnType = function.ReturnType; IEdmEntitySetBase returnSet = null; var singleEntityNode = parent as SingleEntityNode; if (singleEntityNode != null) { returnSet = function.GetTargetEntitySet(singleEntityNode.NavigationSource, state.Model); } string functionName = function.FullName(); if (returnType.IsEntity()) { boundFunction = new SingleEntityFunctionCallNode(functionName, new[] { function }, boundArguments, (IEdmEntityTypeReference)returnType.Definition.ToTypeReference(), returnSet, parent); } else if (returnType.IsEntityCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new EntityCollectionFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, returnSet, parent); } else if (returnType.IsCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new CollectionFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, parent); } else { boundFunction = new SingleValueFunctionCallNode(functionName, new[] { function }, boundArguments, returnType, parent); } return(true); }
/// <summary> /// Constructs a FunctionCallBinder with the given method to be used binding the parent token if needed. /// </summary> /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param> /// <param name="state">State of the metadata binding.</param> internal FunctionCallBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) : base(bindMethod, state) { }
public void GetNavigationNodeCreatesCollectionNavigationNodeForManyMultiplicityProperty() { IEdmNavigationProperty property = HardCodedTestModel.GetDogMyPeopleNavProp(); SingleEntityNode parent = new SingleEntityCastNode(null, HardCodedTestModel.GetDogType()); BindingState state = new BindingState(configuration); KeyBinder keyBinder = new KeyBinder(FakeBindMethods.BindMethodReturningASingleDog); var result = InnerPathTokenBinder.GetNavigationNode(property, parent, null, state, keyBinder); result.ShouldBeCollectionNavigationNode(property); }
public void InnerPathTokenBinderShouldFailIfPropertySourceIsNotASingleValue() { var state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); var metadataBinder = new MetadataBinder(state); var binder = new InnerPathTokenBinder(metadataBinder.Bind, state); var token = new InnerPathToken("MyDog", new InnerPathToken("MyPeople", null, null), null); Action bind = () => binder.BindInnerPathSegment(token); bind.ShouldThrow<ODataException>().WithMessage(Strings.MetadataBinder_PropertyAccessSourceNotSingleValue("MyDog")); }
public void KeyLookupOnNavPropIntegrationTest() { var state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); var metadataBinder = new MetadataBinder(state); var binder = new InnerPathTokenBinder(metadataBinder.Bind, state); var token = new InnerPathToken("MyPeople", null, new[] { new NamedValue(null, new LiteralToken(123)) }); var result = binder.BindInnerPathSegment(token); result.ShouldBeKeyLookupQueryNode(); }
public void CollectionOfTimeOfDayShouldCreateMatchingNode() { var state = new BindingState(configuration); var binder = new InnerPathTokenBinder(FakeBindMethods.BindMethodReturningASinglePerson, state); var token = new InnerPathToken("MyTimeOfDays", new DummyToken(), null /*namedValues*/); var result = binder.BindInnerPathSegment(token); result.ShouldBeCollectionPropertyAccessQueryNode(HardCodedTestModel.GetPersonMyTimeOfDaysProp()); }
private void ResetState() { BindingState state = new BindingState(this.configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetPersonTypeReference(), HardCodedTestModel.GetPeopleSet()); state.RangeVariables.Push(state.ImplicitRangeVariable); binder = new MetadataBinder(state); this.functionCallBinder = new FunctionCallBinder(binder.Bind, state); }
/// <summary> /// Constructs a DottedIdentifierBinder with the given method to be used binding the parent token if needed. /// </summary> /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param> /// <param name="state">State of the metadata binding.</param> internal DottedIdentifierBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) : base(bindMethod, state) { }
/// <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> /// Try to bind a dotted identifier as enum node /// </summary> /// <param name="dottedIdentifierToken">a dotted identifier token</param> /// <param name="parent">the parent node</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundEnum">the output bound enum node</param> /// <returns>true if we bound an enum node, false otherwise.</returns> internal static bool TryBindDottedIdentifierAsEnum(DottedIdentifierToken dottedIdentifierToken, SingleValueNode parent, BindingState state, out QueryNode boundEnum) { return(TryBindIdentifier(dottedIdentifierToken.Identifier, null, state.Model, out boundEnum)); }
/// <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) { ExceptionUtils.CheckArgumentNotNull(dottedIdentifierToken, "castToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); QueryNode parent = null; IEdmType parentType = null; if (state.ImplicitRangeVariable != null) { 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); IEdmStructuredType childStructuredType = childType as IEdmStructuredType; if (childStructuredType == null) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod); QueryNode functionCallNode; if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, parentAsSingleValue, state, out functionCallNode)) { return(functionCallNode); } else if ((!string.IsNullOrEmpty(dottedIdentifierToken.Identifier)) && (dottedIdentifierToken.Identifier[dottedIdentifierToken.Identifier.Length - 1] == '\'')) { // check if it is enum or not EnumBinder enumBinder = new EnumBinder(this.bindMethod); QueryNode enumNode; if (enumBinder.TryBindDottedIdentifierAsEnum(dottedIdentifierToken, parentAsSingleValue, state, out enumNode)) { return(enumNode); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(dottedIdentifierToken.Identifier)); } } else { IEdmTypeReference edmTypeReference = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier).ToTypeReference(); if (edmTypeReference is IEdmPrimitiveTypeReference || edmTypeReference is IEdmEnumTypeReference) { return(new ConstantNode(dottedIdentifierToken.Identifier, dottedIdentifierToken.Identifier)); } 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); IEdmEntityType childEntityType = childStructuredType as IEdmEntityType; if (childEntityType != null) { 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)); } else { IEdmComplexType childComplexType = childStructuredType as IEdmComplexType; Debug.Assert(childComplexType != null, "If it is not entity type, it should be complex type"); CollectionPropertyAccessNode parentAsCollectionProperty = parent as CollectionPropertyAccessNode; if (parentAsCollectionProperty != null) { return(new CollectionPropertyCastNode(parentAsCollectionProperty, childComplexType)); } // parent can be null for casts on the implicit parameter; this is OK if (parent == null) { return(new SingleValueCastNode(null, childComplexType)); } SingleValueNode parentAsSingleValueNode = parent as SingleValueNode; Debug.Assert(parentAsSingleValueNode != null, "If parent of the cast node was not collection, it should be a single value."); return(new SingleValueCastNode(parentAsSingleValueNode, childComplexType)); } }
/// <summary> /// Parses an <paramref name="apply"/> clause on the given <paramref name="elementType"/>, binding /// the text into a metadata-bound or dynamic properties to be applied using the provided model. /// </summary> /// <param name="apply">String representation of the apply expression.</param> /// <param name="configuration">The configuration used for binding.</param> /// <param name="elementType">Type that the apply clause refers to.</param> /// <param name="navigationSource">Navigation source that the elements being filtered are from.</param> /// <returns>A <see cref="ApplyClause"/> representing the metadata bound apply expression.</returns> private static ApplyClause ParseApplyImplementation(string apply, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(elementType, "elementType"); ExceptionUtils.CheckArgumentNotNull(apply, "apply"); // Get the syntactic representation of the apply expression UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(configuration.Settings.FilterLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier); var applyTokens = expressionParser.ParseApply(apply); // Bind it to metadata BindingState state = new BindingState(configuration); state.ImplicitRangeVariable = NodeFactory.CreateImplicitRangeVariable(elementType.ToTypeReference(), navigationSource); state.RangeVariables.Push(state.ImplicitRangeVariable); MetadataBinder binder = new MetadataBinder(state); ApplyBinder applyBinder = new ApplyBinder(binder.Bind, state); ApplyClause boundNode = applyBinder.BindApply(applyTokens); return boundNode; }
public void CreateParentShouldThrowIfBindingStateWithoutImplicitParameter() { var state = new BindingState(this.configuration); Action createparent = () => EndPathBinder.CreateParentFromImplicitRangeVariable(state); createparent.ShouldThrow<ODataException>().WithMessage(Strings.MetadataBinder_PropertyAccessWithoutParentParameter); }
/// <summary> /// Parses the <paramref name="search"/> clause, binding /// the text into a metadata-bound list of properties to be selected using the provided model. /// </summary> /// <param name="search">String representation of the search expression from the URI.</param> /// <param name="configuration">The configuration used for binding.</param> /// <returns>A <see cref="SearchClause"/> representing the metadata bound search expression.</returns> private static SearchClause ParseSearchImplementation(string search, ODataUriParserConfiguration configuration) { ExceptionUtils.CheckArgumentNotNull(configuration, "configuration"); ExceptionUtils.CheckArgumentNotNull(search, "search"); SearchParser searchParser = new SearchParser(configuration.Settings.SearchLimit); QueryToken queryToken = searchParser.ParseSearch(search); // Bind it to metadata BindingState state = new BindingState(configuration); MetadataBinder binder = new MetadataBinder(state); SearchBinder searchBinder = new SearchBinder(binder.Bind); return searchBinder.BindSearch(queryToken); }
public void Init() { this.bindingState = GetBindingStateForTest(HardCodedTestModel.GetPersonTypeReference(), HardCodedTestModel.GetPeopleSet()); this.propertyBinder = new EndPathBinder(BindMethod, this.bindingState); }
/// <summary> /// Constructs a InnerPathTokenBinder. /// </summary> /// <param name="bindMethod">Bind method to use for binding a parent node, if needed.</param> /// <param name="state">State of the metadata binding.</param> internal InnerPathTokenBinder(MetadataBinder.QueryTokenVisitor bindMethod, BindingState state) : base(bindMethod, state) { }
/// <summary> /// Gets a BindingState for the test to use. /// </summary> /// <param name="type">Optional type for the implicit parameter.</param> /// <returns></returns> private BindingState GetBindingStateForTest(IEdmEntityTypeReference typeReference, IEdmEntitySet type) { type.Should().NotBeNull(); EntityCollectionNode entityCollectionNode = new EntitySetNode(type); var implicitParameter = new EntityRangeVariable(ExpressionConstants.It, typeReference, entityCollectionNode); var state = new BindingState(this.configuration) { ImplicitRangeVariable = implicitParameter }; state.RangeVariables.Push(state.ImplicitRangeVariable); return state; }
/// <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) { 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)); }
/// <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) { this.bindMethod = bindMethod; this.state = state; }
/// <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) { 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> /// 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, state.Configuration.Resolver); } 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 && (args.Count == 2)) { // throw if cast enum to not-string : if ((args[0].GetEdmTypeReference() is IEdmEnumTypeReference) && !string.Equals(typeArgument.Value as string, Microsoft.OData.Core.Metadata.EdmConstants.EdmStringTypeName, StringComparison.Ordinal)) { throw new ODataException(ODataErrorStrings.CastBinder_EnumOnlyCastToOrFromString); } // throw if cast not-string to enum : while (returnType is IEdmEnumTypeReference) { IEdmTypeReference edmTypeReference = args[0].GetEdmTypeReference(); if (edmTypeReference == null) { // Support cast null to enum break; } IEdmPrimitiveTypeReference referenceTmp = edmTypeReference as IEdmPrimitiveTypeReference; if (referenceTmp != null) { IEdmPrimitiveType typeTmp = referenceTmp.Definition as IEdmPrimitiveType; if ((typeTmp != null) && (typeTmp.PrimitiveKind == EdmPrimitiveTypeKind.String)) { break; } } throw new ODataException(ODataErrorStrings.CastBinder_EnumOnlyCastToOrFromString); } } if (isCast) { return(returnType); } else { return(EdmCoreModel.Instance.GetBoolean(true)); } }