예제 #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));
        }
예제 #2
0
        private QueryFilterClause ParseRec(Microsoft.OData.UriParser.QueryNode node)
        {
            if (node.Kind == QueryNodeKind.BinaryOperator)
            {
                var binaryOperator = node as BinaryOperatorNode;

                switch (binaryOperator.OperatorKind)
                {
                case BinaryOperatorKind.And:
                    return(new QueryFilterBooleanOperator(ParseRec(binaryOperator.Left), ParseRec(binaryOperator.Right))
                    {
                        Operator = QueryFilterBooleanOperator.AND
                    });

                case BinaryOperatorKind.Or:
                    return(new QueryFilterBooleanOperator(ParseRec(binaryOperator.Left), ParseRec(binaryOperator.Right))
                    {
                        Operator = QueryFilterBooleanOperator.OR
                    });

                default:
                    return(null);
                }
            }
            else if (node.Kind == QueryNodeKind.UnaryOperator)
            {
                var unaryOperator = node as UnaryOperatorNode;
                if (unaryOperator.OperatorKind == UnaryOperatorKind.Not)
                {
                    return new QueryFilterBooleanOperator(ParseRec(unaryOperator.Operand), null)
                           {
                               Operator = QueryFilterBooleanOperator.NOT
                           }
                }
                ;
                else
                {
                    return(null);
                }
            }
            else if (node.Kind == QueryNodeKind.SearchTerm)
            {
                return(new QueryFilterCondition()
                {
                    Operator = null,
                    Property = null,
                    Value = (node as SearchTermNode).Text
                });
            }
            else
            {
                return(null);
            }
        }
예제 #3
0
        /// <summary>
        /// Creates an CollectionResourceFunctionCallNode to represent a operation call that returns a collection of entities.
        /// </summary>
        /// <param name="name">The name of this operation.</param>
        /// <param name="functions">the list of functions that this node should represent.</param>
        /// <param name="parameters">the list of parameters to this operation</param>
        /// <param name="returnedCollectionTypeReference">the type the entity collection returned by this operation. The element type must be an entity type.</param>
        /// <param name="navigationSource">the set containing entities returned by this operation</param>
        /// <param name="source">the semantically bound parent of this CollectionResourceFunctionCallNode.</param>
        /// <exception cref="System.ArgumentNullException">Throws if the provided name is null.</exception>
        /// <exception cref="System.ArgumentNullException">Throws if the provided collection type reference is null.</exception>
        /// <exception cref="System.ArgumentException">Throws if the element type of the provided collection type reference is not an entity type.</exception>
        /// <exception cref="System.ArgumentNullException">Throws if the input operation imports is null</exception>
        public CollectionResourceFunctionCallNode(string name, IEnumerable <IEdmFunction> functions, IEnumerable <QueryNode> parameters, IEdmCollectionTypeReference returnedCollectionTypeReference, IEdmEntitySetBase navigationSource, QueryNode source)
        {
            ExceptionUtils.CheckArgumentNotNull(name, "name");
            ExceptionUtils.CheckArgumentNotNull(returnedCollectionTypeReference, "returnedCollectionTypeReference");
            this.name       = name;
            this.functions  = new ReadOnlyCollection <IEdmFunction>(functions == null ? new List <IEdmFunction>() : functions.ToList());
            this.parameters = new ReadOnlyCollection <QueryNode>(parameters == null ? new List <QueryNode>() : parameters.ToList());
            this.returnedCollectionTypeReference = returnedCollectionTypeReference;
            this.navigationSource = navigationSource;

            this.structuredTypeReference = returnedCollectionTypeReference.ElementType().AsStructuredOrNull();
            if (this.structuredTypeReference == null)
            {
                // TODO: Update error message #644
                throw new ArgumentException(ODataErrorStrings.Nodes_EntityCollectionFunctionCallNode_ItemTypeMustBeAnEntity);
            }

            this.source = source;
        }
예제 #4
0
        /// <summary>
        /// Try to bind an identifier to a EnumNode
        /// </summary>
        /// <param name="identifier">the identifier to bind</param>
        /// <param name="typeReference">the enum typeReference</param>
        /// <param name="modelWhenNoTypeReference">the current model when no enum typeReference.</param>
        /// <param name="boundEnum">an enum node .</param>
        /// <returns>true if we bound an enum for this token.</returns>
        internal static bool TryBindIdentifier(string identifier, IEdmEnumTypeReference typeReference, IEdmModel modelWhenNoTypeReference, out QueryNode boundEnum)
        {
            boundEnum = null;
            string text = identifier;

            // parse the string, e.g., NS.Color'Green'
            // get type information, and also convert Green into an ODataEnumValue

            // find the first ', before that, it is namespace.type
            int indexOfSingleQuote = text.IndexOf('\'');

            if (indexOfSingleQuote < 0)
            {
                return(false);
            }

            string namespaceAndType = text.Substring(0, indexOfSingleQuote);

            Debug.Assert((typeReference == null) || (modelWhenNoTypeReference == null), "((typeReference == null) || (modelWhenNoTypeReference == null)");

            // validate typeReference but allow type name not found in model for delayed throwing.
            if ((typeReference != null) && !string.Equals(namespaceAndType, typeReference.FullName()))
            {
                return(false);
            }

            // get the type
            IEdmEnumType enumType = typeReference != null
                ?
                                    (IEdmEnumType)typeReference.Definition
                :
                                    UriEdmHelpers.FindEnumTypeFromModel(modelWhenNoTypeReference, namespaceAndType);

            if (enumType == null)
            {
                return(false);
            }

            // now, find out the value
            UriParserHelper.TryRemovePrefix(namespaceAndType, ref text);
            UriParserHelper.TryRemoveQuotes(ref text);

            // parse string or int value to edm enum value
            string         enumValueString = text;
            ODataEnumValue enumValue;

            if (!TryParseEnum(enumType, enumValueString, out enumValue))
            {
                return(false);
            }

            // create an enum node, enclosing an odata enum value
            IEdmEnumTypeReference enumTypeReference = typeReference ?? new EdmEnumTypeReference(enumType, false);

            boundEnum = new ConstantNode(enumValue, identifier, enumTypeReference);

            return(true);
        }
예제 #5
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="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));
 }
예제 #6
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)
            {
                if (state.IsCollapsed && !IsAggregatedProperty(endPathToken))
                {
                    throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(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 : 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.InEntitySetAggregation)
                {
                    return(new AggregatedCollectionPropertyNode(collectionParent, property));
                }
            }

            if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction))
            {
                return(boundFunction);
            }

            throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier));
        }
예제 #7
0
 /// <summary>
 /// Try to bind an identifier to a EnumNode
 /// </summary>
 /// <param name="identifier">the identifier to bind</param>
 /// <param name="typeReference">the enum typeReference</param>
 /// <param name="modelWhenNoTypeReference">the current model when no enum typeReference.</param>
 /// <param name="boundEnum">an enum node .</param>
 /// <returns>true if we bound an enum for this token.</returns>
 internal static bool TryBindIdentifier(string identifier, IEdmEnumTypeReference typeReference, IEdmModel modelWhenNoTypeReference, out QueryNode boundEnum)
 {
     return(TryBindIdentifier(identifier, typeReference, modelWhenNoTypeReference, null, out boundEnum));
 }
        private QueryFilterCondition BuildComparison(Microsoft.OData.UriParser.QueryNode left, Microsoft.OData.UriParser.QueryNode right, string normalOperator, string inverseOperator)
        {
            bool   inv          = false;
            string propertyName = null;
            Type   propertyType;
            object value = null;
            short  dateTimeType;

            if (left.Kind == QueryNodeKind.Convert)
            {
                var conv = left as ConvertNode;
                left = conv.Source;
            }
            if (right.Kind == QueryNodeKind.Convert)
            {
                var conv = right as ConvertNode;
                right = conv.Source;
            }
            if (left.Kind == QueryNodeKind.Constant)
            {
                if (right.Kind == QueryNodeKind.SingleValuePropertyAccess)
                {
                    var cnode = right as SingleValuePropertyAccessNode;
                    propertyName = buildPropertyAccess(cnode);
                    propertyType = (cnode.Property as EdmClrProperty).Property.PropertyType;
                }
                else
                {
                    return(null);
                }
                inv   = true;
                value = convertValue((left as ConstantNode).Value, out dateTimeType, propertyType);
            }
            else if (right.Kind == QueryNodeKind.Constant)
            {
                if (left.Kind == QueryNodeKind.SingleValuePropertyAccess)
                {
                    var cnode = left as SingleValuePropertyAccessNode;
                    propertyType = (cnode.Property as EdmClrProperty).Property.PropertyType;
                    propertyName = buildPropertyAccess(cnode);
                }
                else
                {
                    return(null);
                }
                value = convertValue((right as ConstantNode).Value, out dateTimeType, propertyType);
            }
            else
            {
                return(null);
            }
            if (propertyName == null)
            {
                return(null);
            }
            return(new QueryFilterCondition()
            {
                Operator = inv && inverseOperator != null ? inverseOperator : normalOperator,
                Property = propertyName,
                Value = value,
                DateTimeType = dateTimeType,
                Inv = inv && inverseOperator == null
            });
        }
예제 #9
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));
 }
예제 #10
0
        /// <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)
            {
                FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod, state);
                QueryNode          functionCallNode;
                if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, parent as 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, 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)
                    {
                        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));
        }
예제 #11
0
        /// <summary>
        /// Tries to bind key values to a key lookup on a collection.
        /// </summary>
        /// <param name="collectionNode">Already bound collection node.</param>
        /// <param name="namedValues">The named value tokens to bind.</param>
        /// <param name="model">The model to be used.</param>
        /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param>
        /// <param name="keyLookupNode">The bound key lookup.</param>
        /// <returns>Returns true if binding succeeded.</returns>
        private bool TryBindToDeclaredAlternateKey(CollectionResourceNode collectionNode, IEnumerable <NamedValue> namedValues, IEdmModel model, IEdmEntityType collectionItemEntityType, out QueryNode keyLookupNode)
        {
            IEnumerable <IDictionary <string, IEdmProperty> > alternateKeys = model.GetAlternateKeysAnnotation(collectionItemEntityType);

            foreach (IDictionary <string, IEdmProperty> keys in alternateKeys)
            {
                if (TryBindToKeys(collectionNode, namedValues, model, collectionItemEntityType, keys, out keyLookupNode))
                {
                    return(true);
                }
            }

            keyLookupNode = null;
            return(false);
        }
예제 #12
0
        /// <summary>
        /// Binds key values to a key lookup on a collection.
        /// </summary>
        /// <param name="collectionNode">Already bound collection node.</param>
        /// <param name="namedValues">The named value tokens to bind.</param>
        /// <param name="model">The model to be used.</param>
        /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param>
        /// <param name="keys">Dictionary of aliases to structural property names for the key.</param>
        /// <param name="keyLookupNode">The bound key lookup.</param>
        /// <returns>Returns true if binding succeeded.</returns>
        private bool TryBindToKeys(CollectionResourceNode collectionNode, IEnumerable <NamedValue> namedValues, IEdmModel model, IEdmEntityType collectionItemEntityType, IDictionary <string, IEdmProperty> keys, out QueryNode keyLookupNode)
        {
            List <KeyPropertyValue> keyPropertyValues = new List <KeyPropertyValue>();
            HashSet <string>        keyPropertyNames  = new HashSet <string>(StringComparer.Ordinal);

            foreach (NamedValue namedValue in namedValues)
            {
                KeyPropertyValue keyPropertyValue;

                if (!this.TryBindKeyPropertyValue(namedValue, collectionItemEntityType, keys, out keyPropertyValue))
                {
                    keyLookupNode = null;
                    return(false);
                }

                Debug.Assert(keyPropertyValue != null, "keyPropertyValue != null");
                Debug.Assert(keyPropertyValue.KeyProperty != null, "keyPropertyValue.KeyProperty != null");

                if (!keyPropertyNames.Add(keyPropertyValue.KeyProperty.Name))
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_DuplicitKeyPropertyInKeyValues(keyPropertyValue.KeyProperty.Name));
                }

                keyPropertyValues.Add(keyPropertyValue);
            }

            if (keyPropertyValues.Count == 0)
            {
                // No key values specified, for example '/Customers()', do not include the key lookup at all
                keyLookupNode = collectionNode;
                return(true);
            }
            else if (keyPropertyValues.Count != collectionItemEntityType.Key().Count())
            {
                keyLookupNode = null;
                return(false);
            }
            else
            {
                keyLookupNode = new KeyLookupNode(collectionNode, new ReadOnlyCollection <KeyPropertyValue>(keyPropertyValues));
                return(true);
            }
        }
예제 #13
0
        /// <summary>
        /// Tries to bind key values to a key lookup on a collection.
        /// </summary>
        /// <param name="collectionNode">Already bound collection node.</param>
        /// <param name="namedValues">The named value tokens to bind.</param>
        /// <param name="model">The model to be used.</param>
        /// <param name="collectionItemEntityType">The type of a single item in a collection to apply the key value to.</param>
        /// <param name="keyLookupNode">The bound key lookup.</param>
        /// <returns>Returns true if binding succeeded.</returns>
        private bool TryBindToDeclaredKey(CollectionResourceNode collectionNode, IEnumerable <NamedValue> namedValues, IEdmModel model, IEdmEntityType collectionItemEntityType, out QueryNode keyLookupNode)
        {
            Dictionary <string, IEdmProperty> keys = new Dictionary <string, IEdmProperty>(StringComparer.Ordinal);

            foreach (IEdmStructuralProperty property in collectionItemEntityType.Key())
            {
                keys[property.Name] = property;
            }

            return(TryBindToKeys(collectionNode, namedValues, model, collectionItemEntityType, keys, out keyLookupNode));
        }
예제 #14
0
 /// <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));
 }
예제 #15
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);
                }

                CollectionNavigationNode collectionParent = parent as CollectionNavigationNode;

                if (collectionParent != null)
                {
                    IEdmEntityTypeReference parentType         = collectionParent.EntityItemType;
                    IEdmProperty            collectionProperty = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), segmentToken.Identifier);
                    if (collectionProperty != null && collectionProperty.PropertyKind == EdmPropertyKind.Structural)
                    {
                        return(new AggregatedCollectionPropertyNode(collectionParent, collectionProperty));
                    }
                }

                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 new ODataException(
                              ODataErrorStrings.MetadataBinder_PropertyNotDeclared(
                                  parent.GetEdmTypeReference().FullName(), segmentToken.Identifier));
                }

                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);
        }
예제 #16
0
 /// <summary>
 /// Try to bind an inner path token as a function call. Used for bound functions without parameters
 /// that parse as inner path tokens syntactically
 /// </summary>
 /// <param name="innerPathToken">the end path token to bind</param>
 /// <param name="parent">the parent node to this end path token.</param>
 /// <param name="boundFunction">a single value function call node representing the function call, if it exists</param>
 /// <returns>true if we found a function for this token, false otherwise.</returns>
 internal bool TryBindInnerPathAsFunctionCall(InnerPathToken innerPathToken, QueryNode parent, out QueryNode boundFunction)
 {
     return(this.TryBindIdentifier(innerPathToken.Identifier, null, parent, state, out boundFunction));
 }
예제 #17
0
        /// <summary>
        /// Creates a CollectionFunctionCallNode to represent a operation call that returns a collection
        /// </summary>
        /// <param name="name">The name of this operation.</param>
        /// <param name="functions">the list of functions that this node should represent.</param>
        /// <param name="parameters">the list of already bound parameters to this operation</param>
        /// <param name="returnedCollectionType">the type of the collection returned by this operation.</param>
        /// <param name="source">The parent of this CollectionFunctionCallNode.</param>
        /// <exception cref="System.ArgumentNullException">Throws if the provided name is null.</exception>
        /// <exception cref="System.ArgumentNullException">Throws if the provided collection type reference is null.</exception>
        /// <exception cref="System.ArgumentException">Throws if the element type of the provided collection type reference is not a primitive or complex type.</exception>
        public CollectionFunctionCallNode(string name, IEnumerable <IEdmFunction> functions, IEnumerable <QueryNode> parameters, IEdmCollectionTypeReference returnedCollectionType, QueryNode source)
        {
            ExceptionUtils.CheckArgumentNotNull(name, "name");
            ExceptionUtils.CheckArgumentNotNull(returnedCollectionType, "returnedCollectionType");
            this.name                   = name;
            this.functions              = new ReadOnlyCollection <IEdmFunction>(functions == null ? new List <IEdmFunction>() : functions.ToList());
            this.parameters             = new ReadOnlyCollection <QueryNode>(parameters == null ? new List <QueryNode>() : parameters.ToList());
            this.returnedCollectionType = returnedCollectionType;
            this.itemType               = returnedCollectionType.ElementType();

            if (!this.itemType.IsPrimitive() && !this.itemType.IsComplex() && !this.itemType.IsEnum())
            {
                throw new ArgumentException(ODataErrorStrings.Nodes_CollectionFunctionCallNode_ItemTypeMustBePrimitiveOrComplexOrEnum);
            }

            this.source = source;
        }
예제 #18
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);
        }
예제 #19
0
        /// <summary>
        /// Create a SingleValueFunctionCallNode
        /// </summary>
        /// <param name="name">The name of the function to call</param>
        /// <param name="functions">the list of functions that this node should represent.</param>
        /// <param name="parameters">the list of arguments to this function</param>
        /// <param name="returnedTypeReference">the type of the value returned by this function.</param>
        /// <param name="source">The semantically bound parent of this function.</param>
        /// <exception cref="System.ArgumentNullException">Throws if the input operationImports is null.</exception>
        public SingleValueFunctionCallNode(string name, IEnumerable <IEdmFunction> functions, IEnumerable <QueryNode> parameters, IEdmTypeReference returnedTypeReference, QueryNode source)
        {
            ExceptionUtils.CheckArgumentNotNull(name, "name");

            this.name      = name;
            this.functions = new ReadOnlyCollection <IEdmFunction>(functions != null ? functions.ToList() : new List <IEdmFunction>());

            this.parameters = parameters ?? Enumerable.Empty <QueryNode>();

            if (returnedTypeReference != null)
            {
                if (returnedTypeReference.IsCollection() ||
                    !(returnedTypeReference.IsComplex() || returnedTypeReference.IsPrimitive() || returnedTypeReference.IsEnum()))
                {
                    throw new ArgumentException(ODataErrorStrings.Nodes_SingleValueFunctionCallNode_ItemTypeMustBePrimitiveOrComplexOrEnum);
                }
            }

            this.returnedTypeReference = returnedTypeReference;
            this.source = source;
        }
        private QueryFilterClause ParseRec(Microsoft.OData.UriParser.QueryNode node)
        {
            if (node.Kind == QueryNodeKind.BinaryOperator)
            {
                var binaryOperator = node as BinaryOperatorNode;
                switch (binaryOperator.OperatorKind)
                {
                case BinaryOperatorKind.And:
                    return(new QueryFilterBooleanOperator(ParseRec(binaryOperator.Left), ParseRec(binaryOperator.Right))
                    {
                        Operator = QueryFilterBooleanOperator.and
                    });

                case BinaryOperatorKind.Or:
                    return(new QueryFilterBooleanOperator(ParseRec(binaryOperator.Left), ParseRec(binaryOperator.Right))
                    {
                        Operator = QueryFilterBooleanOperator.or
                    });

                case BinaryOperatorKind.Equal:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "eq", "eq"));

                case BinaryOperatorKind.NotEqual:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "ne", "ne"));

                case BinaryOperatorKind.GreaterThan:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "gt", "lt"));

                case BinaryOperatorKind.LessThanOrEqual:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "le", "ge"));

                case BinaryOperatorKind.LessThan:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "lt", "gt"));

                case BinaryOperatorKind.GreaterThanOrEqual:
                    return(BuildComparison(binaryOperator.Left, binaryOperator.Right, "ge", "le"));

                default:
                    return(null);
                }
            }
            else if (node.Kind == QueryNodeKind.UnaryOperator)
            {
                var unaryOperator = node as UnaryOperatorNode;
                if (unaryOperator.OperatorKind == UnaryOperatorKind.Not)
                {
                    return new QueryFilterBooleanOperator(ParseRec(unaryOperator.Operand), null)
                           {
                               Operator = QueryFilterBooleanOperator.not
                           }
                }
                ;
                else
                {
                    return(null);
                }
            }
            else if (node.Kind == QueryNodeKind.SingleValueFunctionCall)
            {
                var functionCall = node as SingleValueFunctionCallNode;
                if (functionCall.Name == null || functionCall.Parameters == null)
                {
                    return(null);
                }
                var args = functionCall.Parameters.ToList();
                if (args.Count != 2)
                {
                    return(null);
                }
                switch (functionCall.Name.ToLower())
                {
                case "contains":
                    return(BuildComparison(args[0], args[1], "contains", null));

                case "startswith":
                    return(BuildComparison(args[0], args[1], "startswith", null));

                case "endswith":
                    return(BuildComparison(args[0], args[1], "endswith", null));

                default:
                    return(null);
                }
            }
            else if (node.Kind == QueryNodeKind.Convert)
            {
                return(ParseRec(((ConvertNode)node).Source));
            }
            else
            {
                return(null);
            }
        }
예제 #21
0
        /// <summary>
        /// Create a SingleResourceFunctionCallNode
        /// </summary>
        /// <param name="name">The name of the operation import to call</param>
        /// <param name="functions">the list of functions this node represents.</param>
        /// <param name="parameters">List of arguments provided to the function. Can be null.</param>
        /// <param name="returnedStructuredTypeReference">The return type of this operation import.</param>
        /// <param name="navigationSource">The entity set or singleton containing the single entity that this operation import returns.</param>
        /// <param name="source">The semantically bound parent of this operation import.</param>
        /// <exception cref="System.ArgumentNullException">Throws if the input name, returnedEntityTypeReference, or navigationSource is null.</exception>
        public SingleResourceFunctionCallNode(string name, IEnumerable <IEdmFunction> functions, IEnumerable <QueryNode> parameters, IEdmStructuredTypeReference returnedStructuredTypeReference, IEdmNavigationSource navigationSource, QueryNode source)
        {
            ExceptionUtils.CheckArgumentNotNull(name, "name");
            ExceptionUtils.CheckArgumentNotNull(returnedStructuredTypeReference, "returnedStructuredTypeReference");

            this.name       = name;
            this.functions  = new ReadOnlyCollection <IEdmFunction>(functions != null ? functions.ToList() : new List <IEdmFunction>());
            this.parameters = new ReadOnlyCollection <QueryNode>(parameters == null ? new List <QueryNode>() : parameters.ToList());
            this.returnedStructuredTypeReference = returnedStructuredTypeReference;
            this.navigationSource = navigationSource;
            this.source           = source;
        }