protected string Bind(QueryNode node) { CollectionNode collectionNode = node as CollectionNode; SingleValueNode singleValueNode = node as SingleValueNode; if (collectionNode != null) { switch (node.Kind) { case QueryNodeKind.CollectionNavigationNode: CollectionNavigationNode navigationNode = node as CollectionNavigationNode; return(BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty)); case QueryNodeKind.CollectionPropertyAccess: return(BindCollectionPropertyAccessNode(node as CollectionPropertyAccessNode)); } } else if (singleValueNode != null) { switch (node.Kind) { case QueryNodeKind.BinaryOperator: return(BindBinaryOperatorNode(node as BinaryOperatorNode)); case QueryNodeKind.Constant: return(BindConstantNode(node as ConstantNode)); case QueryNodeKind.Convert: return(BindConvertNode(node as ConvertNode)); //case QueryNodeKind.EntityRangeVariableReference: // return BindRangeVariable((node as EntityRangeVariableReferenceNode).RangeVariable); //case QueryNodeKind.NonentityRangeVariableReference: // return BindRangeVariable((node as NonentityRangeVariableReferenceNode).RangeVariable); case QueryNodeKind.SingleValuePropertyAccess: return(BindPropertyAccessQueryNode(node as SingleValuePropertyAccessNode)); case QueryNodeKind.UnaryOperator: return(BindUnaryOperatorNode(node as UnaryOperatorNode)); case QueryNodeKind.SingleValueFunctionCall: return(BindSingleValueFunctionCallNode(node as SingleValueFunctionCallNode)); case QueryNodeKind.SingleNavigationNode: SingleNavigationNode navigationNode = node as SingleNavigationNode; return(BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty)); case QueryNodeKind.Any: return(BindAnyNode(node as AnyNode)); case QueryNodeKind.All: return(BindAllNode(node as AllNode)); } } throw new NotSupportedException(String.Format("Nodes of type {0} are not supported", node.Kind)); }
/// <summary> /// Bind the expression of the LambdaToken /// </summary> /// <param name="queryToken">the expression token</param> /// <returns>the bound expression node</returns> private SingleValueNode BindExpressionToken(QueryToken queryToken) { SingleValueNode expression = this.bindMethod(queryToken) as SingleValueNode; if (expression == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_AnyAllExpressionNotSingleValue); } // type reference is allowed to be null for open properties. IEdmTypeReference expressionTypeReference = expression.GetEdmTypeReference(); if (expressionTypeReference != null && !expressionTypeReference.AsPrimitive().IsBoolean()) { throw new ODataException(ODataErrorStrings.MetadataBinder_AnyAllExpressionNotSingleValue); } return(expression); }
/// <summary> /// Checks that all arguments are SingleValueNodes /// </summary> /// <param name="functionName">The name of the function the arguments are from.</param> /// <param name="argumentNodes">The arguments to validate.</param> /// <returns>SingleValueNode array</returns> internal static SingleValueNode[] ValidateArgumentsAreSingleValue(string functionName, List <QueryNode> argumentNodes) { ExceptionUtils.CheckArgumentNotNull(functionName, "functionCallToken"); ExceptionUtils.CheckArgumentNotNull(argumentNodes, "argumentNodes"); // Right now all functions take a single value for all arguments SingleValueNode[] ret = new SingleValueNode[argumentNodes.Count]; for (int i = 0; i < argumentNodes.Count; i++) { SingleValueNode argumentNode = argumentNodes[i] as SingleValueNode; if (argumentNode == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_FunctionArgumentNotSingleValue(functionName)); } ret[i] = argumentNode; } return(ret); }
/// <summary> /// Constructs a new <see cref="CollectionPropertyAccessNode"/>. /// </summary> /// <param name="source">The value containing the property.</param> /// <param name="property">The EDM property which is to be accessed.</param> /// <exception cref="System.ArgumentNullException">Throws if the input source or property is null.</exception> /// <exception cref="ArgumentException">Throws if the input property is not a collection of structural properties</exception> public CollectionPropertyAccessNode(SingleValueNode source, IEdmProperty property) { ExceptionUtils.CheckArgumentNotNull(source, "source"); ExceptionUtils.CheckArgumentNotNull(property, "property"); if (property.PropertyKind != EdmPropertyKind.Structural) { throw new ArgumentException(ODataErrorStrings.Nodes_PropertyAccessShouldBeNonEntityProperty(property.Name)); } if (!property.Type.IsCollection()) { throw new ArgumentException(ODataErrorStrings.Nodes_PropertyAccessTypeMustBeCollection(property.Name)); } this.source = source; this.property = property; this.collectionTypeReference = property.Type.AsCollection(); this.itemType = this.collectionTypeReference.ElementType(); }
/// <summary> /// Binds an In operator token. /// </summary> /// <param name="inToken">The In operator token to bind.</param> /// <param name="state">State of the metadata binding.</param> /// <returns>The bound In operator token.</returns> internal QueryNode BindInOperator(InToken inToken, BindingState state) { ExceptionUtils.CheckArgumentNotNull(inToken, "inToken"); SingleValueNode left = this.GetSingleValueOperandFromToken(inToken.Left); CollectionNode right = null; if (left.TypeReference != null) { right = this.GetCollectionOperandFromToken( inToken.Right, new EdmCollectionTypeReference(new EdmCollectionType(left.TypeReference)), state.Model); } else { right = this.GetCollectionOperandFromToken( inToken.Right, new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetUntyped())), state.Model); } return(new InNode(left, right)); }
/// <summary> /// Retrieves type associated to a segment. /// </summary> /// <param name="segment">The node to retrieve the type from.</param> /// <returns>The type of the node, or item type for collections.</returns> internal static IEdmType GetEdmType(this QueryNode segment) { SingleValueNode singleNode = segment as SingleValueNode; if (singleNode != null) { IEdmTypeReference typeRef = singleNode.TypeReference; return((typeRef != null) ? typeRef.Definition : null); } CollectionNode collectionNode = segment as CollectionNode; if (collectionNode != null) { IEdmTypeReference typeRef = collectionNode.ItemType; return((typeRef != null) ? typeRef.Definition : null); } return(null); }
/// <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 semantics 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(Strings.MetadataBinder_ParameterAliasValueExpressionNotSingleValue); } return(result); }
/// <summary> /// Build a segment representing $filter. /// </summary> /// <param name="expression">The filter expression - this should evaluate to a single boolean value.</param> /// <param name="rangeVariable">An expression that represents a single value from the collection.</param> /// <param name="navigationSource">The navigation source that this filter applies to.</param> /// <exception cref="System.ArgumentNullException">Throws if any input parameter is null.</exception> /// <remarks>$filter should not be applied on singletons or single entities.</remarks> public FilterSegment(SingleValueNode expression, RangeVariable rangeVariable, IEdmNavigationSource navigationSource) { ExceptionUtils.CheckArgumentNotNull(expression, "expression"); ExceptionUtils.CheckArgumentNotNull(rangeVariable, "rangeVariable"); ExceptionUtils.CheckArgumentNotNull(navigationSource, "navigationSource"); this.Identifier = UriQueryConstants.FilterSegment; this.SingleResult = false; this.TargetEdmNavigationSource = navigationSource; this.TargetKind = RequestTargetKind.Resource; this.TargetEdmType = rangeVariable.TypeReference.Definition; this.expression = expression; this.rangeVariable = rangeVariable; this.bindingType = navigationSource.Type; NodeToStringBuilder nodeToStringBuilder = new NodeToStringBuilder(); string expressionString = nodeToStringBuilder.TranslateNode(expression); this.literalText = UriQueryConstants.FilterSegment + ExpressionConstants.SymbolOpenParen + expressionString + ExpressionConstants.SymbolClosedParen; }
/// <summary> /// Promote the left and right operand types, supports enum property and string constant scenario. /// </summary> /// <param name="binaryOperatorKind">the operator kind</param> /// <param name="leftNode">the left operand</param> /// <param name="rightNode">the right operand</param> /// <param name="typeReference">type reference for the result BinaryOperatorNode.</param> public override void PromoteBinaryOperandTypes( BinaryOperatorKind binaryOperatorKind, ref SingleValueNode leftNode, ref SingleValueNode rightNode, out IEdmTypeReference typeReference) { typeReference = null; if (leftNode.TypeReference != null && rightNode.TypeReference != null) { if ((leftNode.TypeReference.IsEnum()) && (rightNode.TypeReference.IsString()) && rightNode is ConstantNode) { string text = ((ConstantNode)rightNode).Value as string; ODataEnumValue val; IEdmTypeReference typeRef = leftNode.TypeReference; if (TryParseEnum(typeRef.Definition as IEdmEnumType, text, out val)) { rightNode = new ConstantNode(val, text, typeRef); return; } } else if ((rightNode.TypeReference.IsEnum()) && (leftNode.TypeReference.IsString()) && leftNode is ConstantNode) { string text = ((ConstantNode)leftNode).Value as string; ODataEnumValue val; IEdmTypeReference typeRef = rightNode.TypeReference; if (TryParseEnum(typeRef.Definition as IEdmEnumType, text, out val)) { leftNode = new ConstantNode(val, text, typeRef); return; } } } // fallback base.PromoteBinaryOperandTypes(binaryOperatorKind, ref leftNode, ref rightNode, out typeReference); }
/// <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> /// 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="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, out QueryNode boundFunction) { return(this.TryBindIdentifier(dottedIdentifierToken.Identifier, null, parent, state, out boundFunction)); }
/// <summary> /// Promote the left and right operand types /// </summary> /// <param name="binaryOperatorKind">the operator kind</param> /// <param name="left">the left operand</param> /// <param name="right">the right operand</param> /// <param name="facetsPromotionRules">Promotion rules for type facets.</param> internal static void PromoteOperandTypes(BinaryOperatorKind binaryOperatorKind, ref SingleValueNode left, ref SingleValueNode right, TypeFacetsPromotionRules facetsPromotionRules) { IEdmTypeReference leftType; IEdmTypeReference rightType; if (!TypePromotionUtils.PromoteOperandTypes(binaryOperatorKind, left, right, out leftType, out rightType, facetsPromotionRules)) { string leftTypeName = left.TypeReference == null ? "<null>" : left.TypeReference.FullName(); string rightTypeName = right.TypeReference == null ? "<null>" : right.TypeReference.FullName(); throw new ODataException(ODataErrorStrings.MetadataBinder_IncompatibleOperandsError(leftTypeName, rightTypeName, binaryOperatorKind)); } left = MetadataBindingUtils.ConvertToTypeIfNeeded(left, leftType); right = MetadataBindingUtils.ConvertToTypeIfNeeded(right, rightType); }
/// <summary> /// Binds a DottedIdentifierToken and it's parent node (if needed). /// </summary> /// <param name="dottedIdentifierToken">Token to bind to metadata.</param> /// <returns>A bound node representing the cast.</returns> internal QueryNode BindDottedIdentifier(DottedIdentifierToken dottedIdentifierToken) { 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(); } } SingleResourceNode parentAsSingleResource = parent as SingleResourceNode; IEdmSchemaType childType = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver); IEdmStructuredType childStructuredType = childType as IEdmStructuredType; if (childStructuredType == null) { SingleValueNode singleValueNode = parent as SingleValueNode; FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod, state); QueryNode functionCallNode; if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, singleValueNode, out functionCallNode)) { return(functionCallNode); } else if ((!string.IsNullOrEmpty(dottedIdentifierToken.Identifier)) && (dottedIdentifierToken.Identifier[dottedIdentifierToken.Identifier.Length - 1] == '\'')) { // check if it is enum or not QueryNode enumNode; if (EnumBinder.TryBindDottedIdentifierAsEnum(dottedIdentifierToken, parentAsSingleResource, state, this.Resolver, out enumNode)) { return(enumNode); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(dottedIdentifierToken.Identifier)); } } else { IEdmTypeReference edmTypeReference = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver).ToTypeReference(); if (edmTypeReference is IEdmPrimitiveTypeReference || edmTypeReference is IEdmEnumTypeReference) { IEdmPrimitiveType childPrimitiveType = childType as IEdmPrimitiveType; if (childPrimitiveType != null && dottedIdentifierToken.NextToken != null) { return(new SingleValueCastNode(singleValueNode, childPrimitiveType)); } else { 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); this.state.ParsedSegments.Add(new TypeSegment(childType, parentType, null)); CollectionResourceNode parentAsCollection = parent as CollectionResourceNode; if (parentAsCollection != null) { return(new CollectionResourceCastNode(parentAsCollection, childStructuredType)); } return(new SingleResourceCastNode(parentAsSingleResource, childStructuredType)); }
/// <summary> /// Binds an end path token into a PropertyAccessToken, OpenPropertyToken, or FunctionCallToken. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <returns>A Query node representing this endpath token, bound to metadata.</returns> internal QueryNode BindEndPath(EndPathToken endPathToken) { ExceptionUtils.CheckArgumentNotNull(endPathToken, "EndPathToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(endPathToken.Identifier, "EndPathToken.Identifier"); // Set the parent (get the parent type, so you can check whether the Identifier inside EndPathToken really is legit offshoot of the parent type) QueryNode parent = this.DetermineParentNode(endPathToken); QueryNode boundFunction; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { // Now that we have the parent type, can find its corresponding EDM type IEdmStructuredTypeReference structuredParentType = singleValueParent.TypeReference == null ? null : singleValueParent.TypeReference.AsStructuredOrNull(); IEdmProperty property = structuredParentType == null ? null : this.Resolver.ResolveProperty(structuredParentType.StructuredDefinition(), endPathToken.Identifier); if (property != null) { return(GeneratePropertyAccessQueryNode(singleValueParent as SingleResourceNode, property, state)); } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, singleValueParent, state, out boundFunction)) { return(boundFunction); } if (endPathToken.Identifier == ExpressionConstants.QueryOptionCount) { return(new CountVirtualPropertyNode()); } return(GeneratePropertyAccessQueryForOpenType(endPathToken, singleValueParent)); } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. CollectionNode colNode = parent as CollectionNode; if (colNode != null && endPathToken.Identifier.Equals(UriQueryConstants.CountSegment)) { // create a collection count node for collection node property. return(new CountNode(colNode)); } CollectionNavigationNode collectionParent = parent as CollectionNavigationNode; if (collectionParent != null) { IEdmEntityTypeReference parentType = collectionParent.EntityItemType; IEdmProperty property = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), endPathToken.Identifier); if (property.PropertyKind == EdmPropertyKind.Structural && !property.Type.IsCollection() && this.state.AggregatedPropertyNames != null && this.state.AggregatedPropertyNames.Any(p => p == collectionParent.NavigationProperty.Name)) { return(new AggregatedCollectionPropertyNode(collectionParent, property)); } } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier)); }
/// <summary> /// This method generates a <see cref="SingleValueOpenPropertyAccessNode"/> for properties of open type /// </summary> /// <param name="endPathToken">EndPathToken to bind into an open property node.</param> /// <param name="parentNode">Parent node of this open property</param> /// <returns>Will return a <see cref="SingleValueOpenPropertyAccessNode"/> when open types are supported</returns> internal SingleValueOpenPropertyAccessNode GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode) { if (parentNode.TypeReference == null || parentNode.TypeReference.Definition.IsOpen() || IsAggregatedProperty(endPathToken.Identifier)) { return(new SingleValueOpenPropertyAccessNode(parentNode, endPathToken.Identifier)); } else { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared( parentNode.TypeReference.FullName(), endPathToken.Identifier)); } }
/// <summary> /// Binds a <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> /// <returns>The bound node.</returns> internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod, state); // 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, 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, this.Resolver); if (property == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction)) { return(boundFunction); } if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpen()) { throw ExceptionUtil.CreatePropertyNotFoundException(segmentToken.Identifier, parent.GetEdmTypeReference().FullName()); } return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier)); } IEdmStructuralProperty structuralProperty = property as IEdmStructuralProperty; if (property.Type.IsComplex()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new SingleComplexNode(singleValueParent as SingleResourceNode, property)); } else if (property.Type.IsPrimitive()) { return(new SingleValuePropertyAccessNode(singleValueParent, property)); } // Note - this means nonentity collection (primitive or complex) if (property.Type.IsNonEntityCollectionType()) { if (property.Type.IsStructuredCollectionType()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new CollectionComplexNode(singleValueParent as SingleResourceNode, property)); } return(new CollectionPropertyAccessNode(singleValueParent, property)); } IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty; if (navigationProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name)); } SingleResourceNode parentResource = EnsureParentIsResourceForNavProp(singleValueParent); IEdmNavigationSource navigationSource; QueryNode node = GetNavigationNode(navigationProperty, parentResource, segmentToken.NamedValues, state, new KeyBinder(this.bindMethod), out navigationSource); // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new NavigationPropertySegment(navigationProperty, navigationSource)); return(node); }
/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the conversion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { // For source is type definition, if source's underlying type == target type. // We create a conversion node from source to its underlying type (target type) // so that the service can convert value of source clr type to underlying clr type. if (source.TypeReference.IsTypeDefinition()) { return(new ConvertNode(source, targetTypeReference)); } return(source); } // Structured type in url will be translated into a node with raw string value. // We create a conversion node from string to structured type. if (targetTypeReference.IsStructured() || targetTypeReference.IsStructuredCollectionType()) { return(new ConvertNode(source, targetTypeReference)); } ConstantNode constantNode = source as ConstantNode; if (constantNode != null && constantNode.Value != null && source.TypeReference.IsString() && targetTypeReference.IsEnum()) { string memberName = constantNode.Value.ToString(); IEdmEnumType enumType = targetTypeReference.Definition as IEdmEnumType; if (enumType.Members.Any(m => string.Compare(m.Name, memberName, StringComparison.Ordinal) == 0)) { string literalText = ODataUriUtils.ConvertToUriLiteral(constantNode.Value, default(ODataVersion)); return(new ConstantNode(new ODataEnumValue(constantNode.Value.ToString(), targetTypeReference.Definition.ToString()), literalText, targetTypeReference)); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(memberName)); } } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.FullName(), targetTypeReference.FullName())); } else { if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object originalPrimitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out originalPrimitiveValue) && (originalPrimitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object targetPrimitiveValue = ODataUriConversionUtils.CoerceNumericType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(targetPrimitiveValue)); } var candidate = new ConstantNode(targetPrimitiveValue, constantNode.LiteralText); var decimalType = candidate.TypeReference as IEdmDecimalTypeReference; if (decimalType != null) { var targetDecimalType = (IEdmDecimalTypeReference)targetTypeReference; return(decimalType.Precision == targetDecimalType.Precision && decimalType.Scale == targetDecimalType.Scale ? (SingleValueNode)candidate : (SingleValueNode)(new ConvertNode(candidate, targetTypeReference))); } else { return(candidate); } } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
/// <summary> /// Create a BinaryOperatorNode /// </summary> /// <param name="operatorKind">The binary operator type.</param> /// <param name="left">The left operand.</param> /// <param name="right">The right operand.</param> /// <param name="typeReference">The result typeReference.</param> /// <exception cref="System.ArgumentNullException">Throws if the left or right inputs are null.</exception> internal BinaryOperatorNode(BinaryOperatorKind operatorKind, SingleValueNode left, SingleValueNode right, IEdmTypeReference typeReference) { ExceptionUtils.CheckArgumentNotNull(left, "left"); ExceptionUtils.CheckArgumentNotNull(right, "right"); this.operatorKind = operatorKind; this.left = left; this.right = right; // set the TypeReference if explicitly given, otherwise based on the Operands. if (typeReference != null) { this.typeReference = typeReference; } else if (this.Left == null || this.Right == null || this.Left.TypeReference == null || this.Right.TypeReference == null) { this.typeReference = null; } else { // Get a primitive type reference; this must not fail since we checked that the type is of kind 'primitive'. IEdmPrimitiveTypeReference leftType = this.Left.TypeReference.AsPrimitive(); IEdmPrimitiveTypeReference rightType = this.Right.TypeReference.AsPrimitive(); this.typeReference = QueryNodeUtils.GetBinaryOperatorResultType(leftType, rightType, this.OperatorKind); } }
/// <summary> /// Try to bind an identifier to a FunctionCallNode /// </summary> /// <param name="identifier">the identifier to bind</param> /// <param name="arguments">the semantically bound list of arguments.</param> /// <param name="parent">a semantically bound parent node.</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundFunction">a single value function call node representing this function call, if we found one.</param> /// <returns>true if we found a function for this token.</returns> private bool TryBindIdentifier(string identifier, IEnumerable <FunctionParameterToken> arguments, QueryNode parent, BindingState state, out QueryNode boundFunction) { boundFunction = null; IEdmType bindingType = null; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { if (singleValueParent.TypeReference != null) { bindingType = singleValueParent.TypeReference.Definition; } } else { CollectionNode 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; SingleResourceNode singleEntityNode = parent as SingleResourceNode; if (singleEntityNode != null) { returnSet = function.GetTargetEntitySet(singleEntityNode.NavigationSource, state.Model); } string functionName = function.FullName(); if (returnType.IsEntity()) { boundFunction = new SingleResourceFunctionCallNode(functionName, new[] { function }, boundArguments, (IEdmEntityTypeReference)returnType.Definition.ToTypeReference(), returnSet, parent); } else if (returnType.IsStructuredCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new CollectionResourceFunctionCallNode(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); }
private string BindNavigationPropertyNode(SingleValueNode singleValueNode, IEdmNavigationProperty edmNavigationProperty) { return(Bind(singleValueNode) + "." + edmNavigationProperty.Name); }
/// <summary> /// This method generates a <see cref="SingleValueOpenPropertyAccessNode"/> for properties of open type /// </summary> /// <param name="endPathToken">EndPathToken to bind into an open property node.</param> /// <param name="parentNode">Parent node of this open property</param> /// <returns>Will return a <see cref="SingleValueOpenPropertyAccessNode"/> when open types are supported</returns> internal SingleValueOpenPropertyAccessNode GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode) { if (parentNode.TypeReference == null || parentNode.TypeReference.Definition.IsOpen() || IsAggregatedProperty(endPathToken.Identifier)) { return(new SingleValueOpenPropertyAccessNode(parentNode, endPathToken.Identifier)); } else { throw ExceptionUtil.CreatePropertyNotFoundException(endPathToken.Identifier, parentNode.TypeReference.FullName()); } }
/// <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="resolver">ODataUriResolver</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, ODataUriResolver resolver, out QueryNode boundEnum) { return(TryBindIdentifier(dottedIdentifierToken.Identifier, null, state.Model, resolver, out boundEnum)); }
/// <summary> /// Create a BinaryOperatorNode /// </summary> /// <param name="operatorKind">The binary operator type.</param> /// <param name="left">The left operand.</param> /// <param name="right">The right operand.</param> /// <exception cref="System.ArgumentNullException">Throws if the left or right inputs are null.</exception> /// <exception cref="ODataException">Throws if the two operands don't have the same type.</exception> public BinaryOperatorNode(BinaryOperatorKind operatorKind, SingleValueNode left, SingleValueNode right) : this(operatorKind, left, right, /*typeReference*/ null) { }