Exemple #1
0
        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));
        }
Exemple #2
0
        /// <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);
        }
Exemple #3
0
        /// <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();
        }
Exemple #5
0
        /// <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));
        }
Exemple #6
0
        /// <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;
        }
Exemple #9
0
        /// <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);
        }
Exemple #10
0
        /// <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);
        }
Exemple #11
0
 /// <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));
 }
Exemple #12
0
        /// <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));
        }
Exemple #14
0
        /// <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));
        }
Exemple #15
0
 /// <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));
     }
 }
Exemple #16
0
        /// <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);
        }
Exemple #17
0
        /// <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);
            }
        }
Exemple #19
0
        /// <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);
        }
Exemple #20
0
 private string BindNavigationPropertyNode(SingleValueNode singleValueNode, IEdmNavigationProperty edmNavigationProperty)
 {
     return(Bind(singleValueNode) + "." + edmNavigationProperty.Name);
 }
Exemple #21
0
 /// <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());
     }
 }
Exemple #22
0
 /// <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)
 {
 }