/// <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); }
/// <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); }
/// <summary> /// If the source node is not of the specified type, then we check if type promotion is possible and inject a convert node. /// If the source node is the same type as the target type (or if the target type is null), we just return the source node as is. /// </summary> /// <param name="source">The source node to apply the conversion to.</param> /// <param name="targetTypeReference">The target primitive type. May be null - this method will do nothing in that case.</param> /// <returns>The converted query node, or the original source node unchanged.</returns> internal static SingleValueNode ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference) { Debug.Assert(source != null, "source != null"); if (targetTypeReference == null) { return(source); } if (source.TypeReference != null) { if (source.TypeReference.IsEquivalentTo(targetTypeReference)) { // For source is type definition, if source's underlying type == target type. // We create a conversion node from source to its underlying type (target type) // so that the service can convert value of source clr type to underlying clr type. if (source.TypeReference.IsTypeDefinition()) { return(new ConvertNode(source, targetTypeReference)); } return(source); } // Structured type in url will be translated into a node with raw string value. // We create a conversion node from string to structured type. if (targetTypeReference.IsStructured() || targetTypeReference.IsStructuredCollectionType()) { return(new ConvertNode(source, targetTypeReference)); } ConstantNode constantNode = source as ConstantNode; if (constantNode != null && constantNode.Value != null && source.TypeReference.IsString() && targetTypeReference.IsEnum()) { string memberName = constantNode.Value.ToString(); IEdmEnumType enumType = targetTypeReference.Definition as IEdmEnumType; if (enumType.Members.Any(m => string.Compare(m.Name, memberName, StringComparison.Ordinal) == 0)) { string literalText = ODataUriUtils.ConvertToUriLiteral(constantNode.Value, default(ODataVersion)); return(new ConstantNode(new ODataEnumValue(constantNode.Value.ToString(), targetTypeReference.Definition.ToString()), literalText, targetTypeReference)); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(memberName)); } } if (!TypePromotionUtils.CanConvertTo(source, source.TypeReference, targetTypeReference)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CannotConvertToType(source.TypeReference.FullName(), targetTypeReference.FullName())); } else { if (source.TypeReference.IsEnum() && constantNode != null) { return(new ConstantNode(constantNode.Value, ODataUriUtils.ConvertToUriLiteral(constantNode.Value, ODataVersion.V4), targetTypeReference)); } object originalPrimitiveValue; if (MetadataUtilsCommon.TryGetConstantNodePrimitiveLDMF(source, out originalPrimitiveValue) && (originalPrimitiveValue != null)) { // L F D M types : directly create a ConvertNode. // 1. NodeToExpressionTranslator.cs won't allow implicitly converting single/double to decimal, which should be done here at Node tree level. // 2. And prevent losing precision in float -> double, e.g. (double)1.234f => 1.2339999675750732d not 1.234d object targetPrimitiveValue = ODataUriConversionUtils.CoerceNumericType(originalPrimitiveValue, targetTypeReference.AsPrimitive().Definition as IEdmPrimitiveType); if (string.IsNullOrEmpty(constantNode.LiteralText)) { return(new ConstantNode(targetPrimitiveValue)); } var candidate = new ConstantNode(targetPrimitiveValue, constantNode.LiteralText); var decimalType = candidate.TypeReference as IEdmDecimalTypeReference; if (decimalType != null) { var targetDecimalType = (IEdmDecimalTypeReference)targetTypeReference; return(decimalType.Precision == targetDecimalType.Precision && decimalType.Scale == targetDecimalType.Scale ? (SingleValueNode)candidate : (SingleValueNode)(new ConvertNode(candidate, targetTypeReference))); } else { return(candidate); } } else { // other type conversion : ConvertNode return(new ConvertNode(source, targetTypeReference)); } } } else { // If the source doesn't have a type (possibly an open property), then it's possible to convert it // cause we don't know for sure. return(new ConvertNode(source, targetTypeReference)); } }
/// <summary> /// 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)); } }