/// <summary>
        /// Get the promoted type reference of the operand
        /// </summary>
        /// <param name="operand">the operand</param>
        /// <param name="unaryOperatorKind">the operator kind</param>
        /// <returns>the type reference of the operand</returns>
        private static IEdmTypeReference PromoteOperandType(SingleValueNode operand, UnaryOperatorKind unaryOperatorKind)
        {
            IEdmTypeReference typeReference = operand.TypeReference;

            if (!TypePromotionUtils.PromoteOperandType(unaryOperatorKind, ref typeReference))
            {
                string typeName = operand.TypeReference == null ? "<null>" : operand.TypeReference.FullName();
                throw new ODataException(ODataErrorStrings.MetadataBinder_IncompatibleOperandError(typeName, unaryOperatorKind));
            }

            return(typeReference);
        }
        public void EqualsOnComplexAndOtherComplexIsNotSupported()
        {
            var otherComplexType        = new EdmComplexTypeReference(new EdmComplexType("NS", "OtherComplex"), true);
            IEdmTypeReference left      = HardCodedTestModel.GetPersonAddressProp().Type;
            IEdmTypeReference right     = otherComplexType;
            SingleValueNode   leftNode  = new SingleValuePropertyAccessNode(new ConstantNode(null) /*parent*/, new EdmStructuralProperty(new EdmEntityType("MyNamespace", "MyEntityType"), "myPropertyName", left));
            SingleValueNode   rightNode = new SingleValuePropertyAccessNode(new ConstantNode(null) /*parent*/, new EdmStructuralProperty(new EdmEntityType("MyNamespace", "MyEntityType"), "myPropertyName", right));
            var result = TypePromotionUtils.PromoteOperandTypes(BinaryOperatorKind.Equal, leftNode, rightNode, out left, out right);

            result.Should().BeFalse();
            left.ShouldBeEquivalentTo(HardCodedTestModel.GetPersonAddressProp().Type);
            right.ShouldBeEquivalentTo(otherComplexType);
        }
        /// <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>
        internal static void PromoteOperandTypes(BinaryOperatorKind binaryOperatorKind, ref SingleValueNode left, ref SingleValueNode right)
        {
            IEdmTypeReference leftType;
            IEdmTypeReference rightType;

            if (!TypePromotionUtils.PromoteOperandTypes(binaryOperatorKind, left, right, out leftType, out rightType))
            {
                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);
        }
Beispiel #4
0
        /// <summary>
        /// Finds the signature that best matches the arguments
        /// </summary>
        /// <param name="functionCallToken">The name of the function</param>
        /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param>
        /// <param name="nameSignatures">The name-signature pairs to match against</param>
        /// <returns>Returns the matching signature or throws</returns>
        internal static KeyValuePair <string, FunctionSignatureWithReturnType> MatchSignatureToUriFunction(string functionCallToken, SingleValueNode[] argumentNodes,
                                                                                                           IList <KeyValuePair <string, FunctionSignatureWithReturnType> > nameSignatures)
        {
            KeyValuePair <string, FunctionSignatureWithReturnType> nameSignature;

            IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray();

            // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments
            int argumentCount = argumentTypes.Length;

            if (argumentTypes.All(a => a == null) && argumentCount > 0)
            {
                // we specifically want to find just the first function that matches the number of arguments, we don't care about
                // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
                // those arguments are.
                KeyValuePair <string, FunctionSignatureWithReturnType> found = nameSignatures.FirstOrDefault(pair => pair.Value.ArgumentTypes.Length == argumentCount);
                if (found.Equals(TypePromotionUtils.NotFoundKeyValuePair))
                {
                    throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionCallToken, argumentTypes.Length));
                }
                else
                {
                    // in this case we can't assert the return type, we can only assert that a function exists... so
                    // we need to set the return type to null.
                    nameSignature = new KeyValuePair <string, FunctionSignatureWithReturnType>(
                        found.Key, new FunctionSignatureWithReturnType(null, found.Value.ArgumentTypes));
                }
            }
            else
            {
                nameSignature =
                    TypePromotionUtils.FindBestFunctionSignature(nameSignatures, argumentNodes, functionCallToken);
                if (nameSignature.Equals(TypePromotionUtils.NotFoundKeyValuePair))
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                                                 functionCallToken,
                                                 UriFunctionsHelper.BuildFunctionSignatureListDescription(functionCallToken, nameSignatures.Select(sig => sig.Value))));
                }
            }

            return(nameSignature);
        }
        /// <summary>
        /// Finds the signature that best matches the arguments
        /// </summary>
        /// <param name="functionName">The name of the function</param>
        /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param>
        /// <param name="signatures">The signatures to match against</param>
        /// <returns>Returns the matching signature or throws</returns>
        internal static FunctionSignatureWithReturnType MatchSignatureToUriFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures)
        {
            FunctionSignatureWithReturnType signature;

            IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray();

            // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments
            int argumentCount = argumentTypes.Length;

            if (argumentTypes.All(a => a == null) && argumentCount > 0)
            {
                // we specifically want to find just the first function that matches the number of arguments, we don't care about
                // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
                // those arguments are.
                signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count()));
                }
                else
                {
                    // in this case we can't assert the return type, we can only assert that a function exists... so
                    // we need to set the return type to null.
                    signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes);
                }
            }
            else
            {
                signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                                                 functionName,
                                                 UriFunctionsHelper.BuildFunctionSignatureListDescription(functionName, signatures)));
                }
            }

            return(signature);
        }
Beispiel #6
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));
            }
        }
Beispiel #7
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 convertion 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);
                }

                if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference))
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.ODataFullName(), targetTypeReference.ODataFullName()));
                }
                else
                {
                    ConstantNode constantNode = source as ConstantNode;

                    if (source.TypeReference.IsEnum() && constantNode != null)
                    {
                        return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference));
                    }

                    object originalPrimitiveValue;
                    if (MetadataUtilsCommon.TryGetConstantNodePrimitiveDate(source, out originalPrimitiveValue) && (originalPrimitiveValue != null))
                    {
                        // DateTimeOffset -> Date when (target is Date) and (originalValue match Date format) and (ConstantNode)
                        object targetPrimitiveValue = ODataUriConversionUtils.CoerceTemporalType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType);

                        if (targetPrimitiveValue != null)
                        {
                            if (string.IsNullOrEmpty(constantNode.LiteralText))
                            {
                                return(new ConstantNode(targetPrimitiveValue));
                            }

                            return(new ConstantNode(targetPrimitiveValue, constantNode.LiteralText, targetTypeReference));
                        }
                    }

                    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));
                        }

                        return(new ConstantNode(targetPrimitiveValue, constantNode.LiteralText));
                    }
                    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));
            }
        }