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)); }
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); } }
/// <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; }
/// <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); }
/// <summary> /// Try to bind a dotted identifier as enum node /// </summary> /// <param name="dottedIdentifierToken">a dotted identifier token</param> /// <param name="parent">the parent node</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundEnum">the output bound enum node</param> /// <returns>true if we bound an enum node, false otherwise.</returns> internal static bool TryBindDottedIdentifierAsEnum(DottedIdentifierToken dottedIdentifierToken, SingleValueNode parent, BindingState state, out QueryNode boundEnum) { return(TryBindIdentifier(dottedIdentifierToken.Identifier, null, state.Model, out boundEnum)); }
/// <summary> /// Binds 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)); }
/// <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 }); }
/// <summary> /// Try to bind a <see cref="DottedIdentifierToken"/> as a function call. Used for container qualified functions without parameters. /// </summary> /// <param name="dottedIdentifierToken">the dotted identifier token to bind</param> /// <param name="parent">the semantically bound parent node for this dotted identifier</param> /// <param name="boundFunction">a single value function call node representing the function call, if we found one.</param> /// <returns>true if we found a function for this token, false otherwise.</returns> internal bool TryBindDottedIdentifierAsFunctionCall(DottedIdentifierToken dottedIdentifierToken, SingleValueNode parent, out QueryNode boundFunction) { return(this.TryBindIdentifier(dottedIdentifierToken.Identifier, null, parent, state, out boundFunction)); }
/// <summary> /// 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)); }
/// <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); }
/// <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); } }
/// <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)); }
/// <summary> /// Try to bind an end path token as a function call. Used for bound functions without parameters /// that parse as end path tokens syntactically /// </summary> /// <param name="endPathToken">the end path token to bind</param> /// <param name="parent">the parent node to this end path token.</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundFunction">a single value function call node representing the function call, if it exists</param> /// <returns>true if we found a function for this token, false otherwise.</returns> internal bool TryBindEndPathAsFunctionCall(EndPathToken endPathToken, QueryNode parent, BindingState state, out QueryNode boundFunction) { return(this.TryBindIdentifier(endPathToken.Identifier, null, parent, state, out boundFunction)); }
/// <summary> /// 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); }
/// <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)); }
/// <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; }
/// <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); }
/// <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); } }
/// <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; }