Example #1
0
        /// <summary>Creates a new function description for a method or property.</summary>
        /// <param name="targetType">Type on which property or method is declared.</param>
        /// <param name="instance">Whether an instance member is looked for.</param>
        /// <param name="method">Whether a method (rather than a property) is looked for.</param>
        /// <param name="name">Name of member.</param>
        /// <param name="parameterTypes">Parameter types.</param>
        /// <returns>A new function description.</returns>
        private static FunctionDescription CreateFunctionDescription(
            Type targetType,
            bool instance,
            bool method,
            string name,
            params Type[] parameterTypes)
        {
            Debug.Assert(targetType != null, "targetType != null");
            Debug.Assert(name != null, "name != null");
            Debug.Assert(parameterTypes.Length == 0 || method, "parameterTypes.Length == 0 || method");
            Debug.Assert(method || instance, "method || instance");

            BindingFlags flags = BindingFlags.Public | (instance ? BindingFlags.Instance : BindingFlags.Static);
            MemberInfo   member;

            if (method)
            {
                member = targetType.GetMethod(name, flags, null, parameterTypes, null);
                Debug.Assert(member != null, "methodInfo != null");
            }
            else
            {
                member = targetType.GetProperty(name, flags);
                Debug.Assert(member != null, "propertyInfo != null");
            }

            Type[] functionParameterTypes;
            if (instance)
            {
                functionParameterTypes    = new Type[parameterTypes.Length + 1];
                functionParameterTypes[0] = targetType;
                parameterTypes.CopyTo(functionParameterTypes, 1);
            }
            else
            {
                functionParameterTypes = parameterTypes;
            }

            FunctionDescription result = new FunctionDescription(member, functionParameterTypes);

            if (method)
            {
                if (instance)
                {
                    result.ConversionFunction = new Func <Expression, Expression[], Expression>(result.InstanceMethodConversionFunction);
                }
                else
                {
                    result.ConversionFunction = new Func <Expression, Expression[], Expression>(result.StaticMethodConversionFunction);
                }
            }
            else
            {
                Debug.Assert(instance, "instance");
                result.ConversionFunction = new Func <Expression, Expression[], Expression>(result.InstancePropertyConversionFunction);
            }

            return(result);
        }
Example #2
0
        /// <summary>Creates and populates a dictionary of system functions.</summary>
        /// <returns>A new dictionary of functions.</returns>
        internal static Dictionary <string, FunctionDescription[]> CreateFunctions()
        {
            Dictionary <string, FunctionDescription[]> result = new Dictionary <string, FunctionDescription[]>(StringComparer.Ordinal);

            // String functions.
            result.Add("endswith", new FunctionDescription[] { StringInstanceFunction("EndsWith", typeof(string)) });
            result.Add("indexof", new FunctionDescription[] { StringInstanceFunction("IndexOf", typeof(string)) });
            result.Add("replace", new FunctionDescription[] { StringInstanceFunction(FunctionNameClrStringReplace, typeof(string), typeof(string)) });
            result.Add("startswith", new FunctionDescription[] { StringInstanceFunction("StartsWith", typeof(string)) });
            result.Add("tolower", new FunctionDescription[] { StringInstanceFunction("ToLower", Type.EmptyTypes) });
            result.Add("toupper", new FunctionDescription[] { StringInstanceFunction("ToUpper", Type.EmptyTypes) });
            result.Add("trim", new FunctionDescription[] { StringInstanceFunction("Trim", Type.EmptyTypes) });

            FunctionDescription[] signatures = new FunctionDescription[]
            {
                StringInstanceFunction("Substring", typeof(int)),
                StringInstanceFunction("Substring", typeof(int), typeof(int))
            };
            result.Add("substring", signatures);

            signatures = new FunctionDescription[]
            {
                new FunctionDescription("Contains", new Type[] { typeof(string), typeof(string) }, Contains)
            };
            result.Add("contains", signatures);

            signatures = new FunctionDescription[]
            {
                CreateFunctionDescription(typeof(string), false /* instance */, true /* method */, "Concat", typeof(string), typeof(string))
            };
            result.Add("concat", signatures);

            signatures = new FunctionDescription[]
            {
                CreateFunctionDescription(typeof(string), true /* instance */, false /* method */, "Length", Type.EmptyTypes)
            };
            result.Add("length", signatures);

            // DateTime, DateTimeOffset and Duration functions
            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Year"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Year")
            };
            result.Add("year", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Month"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Month"),
            };
            result.Add("month", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Day"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Day"),
            };
            result.Add("day", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Hour"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Hour"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Hours"),
            };
            result.Add("hour", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Minute"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Minute"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Minutes"),
            };
            result.Add("minute", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Second"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Second"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Seconds"),
            };
            result.Add("second", signatures);

            // TODO: what function do we need to add for GetTotalOffsetMinutes

            // Mathematical functions.
            result.Add("round", MathFunctionArray("Round"));
            result.Add("floor", MathFunctionArray("Floor"));
            result.Add("ceiling", MathFunctionArray("Ceiling"));

            // Type functions.
            signatures = new FunctionDescription[]
            {
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(Type) }, FunctionDescription.UnaryIsOf),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(object), typeof(Type) }, FunctionDescription.BinaryIsOf),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(ResourceType) }, FunctionDescription.UnaryIsOfResourceType),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(object), typeof(ResourceType) }, FunctionDescription.BinaryIsOfResourceType),
            };
            result.Add(FunctionNameIsOf, signatures);

            // For cast() signatures, we need to add all primitive types directly as well as the object (open type)
            // and unary versions; otherwise expression will convert to object, then again to whatever other type
            // is required.
            Microsoft.OData.Service.Providers.ResourceType[] primitiveTypes = PrimitiveResourceTypeMap.TypeMap.AllPrimitives;
            List <FunctionDescription> castSignatures = new List <FunctionDescription>(primitiveTypes.Length + 4);

            for (int i = 0; i < primitiveTypes.Length; i++)
            {
                Debug.Assert(
                    primitiveTypes[i].InstanceType != typeof(Type),
                    "primitiveTypes[i].Type != typeof(Type) -- otherwise extra signatures will be added for cast()");
                Debug.Assert(
                    primitiveTypes[i].InstanceType != typeof(object),
                    "primitiveTypes[i].Type != typeof(object) -- otherwise extra signatures will be added for cast()");
                castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { primitiveTypes[i].InstanceType, typeof(Type) }, FunctionDescription.BinaryCast));
            }

            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(Type) }, FunctionDescription.UnaryCast));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(object), typeof(Type) }, FunctionDescription.BinaryCast));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(ResourceType) }, FunctionDescription.UnaryCastResourceType));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(object), typeof(ResourceType) }, FunctionDescription.BinaryCastResourceType));

            result.Add(FunctionNameCast, castSignatures.ToArray());

            #region Spatial Functions
            // geo.distance()
            var geoDistanceSignatures = new List <FunctionDescription>()
            {
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Distance", typeof(GeographyPoint), typeof(GeographyPoint)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Distance", typeof(GeometryPoint), typeof(GeometryPoint)),
            };

            // spatial functions do not require null propagation
            foreach (var geoDistanceSignature in geoDistanceSignatures)
            {
                geoDistanceSignature.RequiresNullPropagation = false;
            }

            result.Add("geo.distance", geoDistanceSignatures.ToArray());

            // geo.length()
            var geoLengthSignatures = new List <FunctionDescription>()
            {
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Length", typeof(GeographyLineString)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Length", typeof(GeometryLineString)),
            };

            // spatial functions do not require null propagation
            foreach (var tmp in geoLengthSignatures)
            {
                tmp.RequiresNullPropagation = false;
            }

            result.Add("geo.length", geoLengthSignatures.ToArray());

            // geo.intersects()
            var geoIntersectsSignatures = new List <FunctionDescription>()
            {
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Intersects", typeof(GeographyPoint), typeof(GeographyPolygon)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Intersects", typeof(GeometryPoint), typeof(GeometryPolygon)),
            };

            // spatial functions do not require null propagation
            foreach (var tmp in geoIntersectsSignatures)
            {
                tmp.RequiresNullPropagation = false;
            }

            result.Add("geo.intersects", geoIntersectsSignatures.ToArray());
            #endregion

            return(result);
        }
        /// <summary>Creates and populates a dictionary of system functions.</summary>
        /// <returns>A new dictionary of functions.</returns>
        internal static Dictionary<string, FunctionDescription[]> CreateFunctions()
        {
            Dictionary<string, FunctionDescription[]> result = new Dictionary<string, FunctionDescription[]>(StringComparer.Ordinal);

            // String functions.
            result.Add("endswith", new FunctionDescription[] { StringInstanceFunction("EndsWith", typeof(string)) });
            result.Add("indexof", new FunctionDescription[] { StringInstanceFunction("IndexOf", typeof(string)) });
            result.Add("replace", new FunctionDescription[] { StringInstanceFunction(FunctionNameClrStringReplace, typeof(string), typeof(string)) });
            result.Add("startswith", new FunctionDescription[] { StringInstanceFunction("StartsWith", typeof(string)) });
            result.Add("tolower", new FunctionDescription[] { StringInstanceFunction("ToLower", Type.EmptyTypes) });
            result.Add("toupper", new FunctionDescription[] { StringInstanceFunction("ToUpper", Type.EmptyTypes) });
            result.Add("trim", new FunctionDescription[] { StringInstanceFunction("Trim", Type.EmptyTypes) });

            FunctionDescription[] signatures = new FunctionDescription[]
                                                   {
                                                       StringInstanceFunction("Substring", typeof(int)),
                                                       StringInstanceFunction("Substring", typeof(int), typeof(int)) 
                                                   };
            result.Add("substring", signatures);

            signatures = new FunctionDescription[]
            {
                new FunctionDescription("Contains", new Type[] { typeof(string), typeof(string) }, Contains)
            };
            result.Add("contains", signatures);

            signatures = new FunctionDescription[]
            {
                CreateFunctionDescription(typeof(string), false /* instance */, true /* method */, "Concat", typeof(string), typeof(string))
            };
            result.Add("concat", signatures);

            signatures = new FunctionDescription[]
            { 
                CreateFunctionDescription(typeof(string), true /* instance */, false /* method */, "Length", Type.EmptyTypes)
            };
            result.Add("length", signatures);

            // DateTime, DateTimeOffset and Duration functions
            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Year"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Year")
            };
            result.Add("year", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Month"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Month"),
            };
            result.Add("month", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Day"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Day"),
            };
            result.Add("day", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Hour"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Hour"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Hours"),
            };
            result.Add("hour", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Minute"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Minute"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Minutes"),
            };
            result.Add("minute", signatures);

            signatures = new FunctionDescription[]
            {
                CreatePropertyBasedFunction(typeof(DateTime), "Second"),
                CreatePropertyBasedFunction(typeof(DateTimeOffset), "Second"),
                CreatePropertyBasedFunction(typeof(TimeSpan), "Seconds"),
            };
            result.Add("second", signatures);

            // TODO: what function do we need to add for GetTotalOffsetMinutes

            // Mathematical functions.
            result.Add("round", MathFunctionArray("Round"));
            result.Add("floor", MathFunctionArray("Floor"));
            result.Add("ceiling", MathFunctionArray("Ceiling"));

            // Type functions.
            signatures = new FunctionDescription[]
            {
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(Type) }, FunctionDescription.UnaryIsOf),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(object), typeof(Type) }, FunctionDescription.BinaryIsOf),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(ResourceType) }, FunctionDescription.UnaryIsOfResourceType),
                new FunctionDescription(FunctionNameIsOf, new Type[] { typeof(object), typeof(ResourceType) }, FunctionDescription.BinaryIsOfResourceType),
            };
            result.Add(FunctionNameIsOf, signatures);

            // For cast() signatures, we need to add all primitive types directly as well as the object (open type)
            // and unary versions; otherwise expression will convert to object, then again to whatever other type
            // is required.
            Microsoft.OData.Service.Providers.ResourceType[] primitiveTypes = PrimitiveResourceTypeMap.TypeMap.AllPrimitives;
            List<FunctionDescription> castSignatures = new List<FunctionDescription>(primitiveTypes.Length + 4);
            for (int i = 0; i < primitiveTypes.Length; i++)
            {
                Debug.Assert(
                    primitiveTypes[i].InstanceType != typeof(Type),
                    "primitiveTypes[i].Type != typeof(Type) -- otherwise extra signatures will be added for cast()");
                Debug.Assert(
                    primitiveTypes[i].InstanceType != typeof(object),
                    "primitiveTypes[i].Type != typeof(object) -- otherwise extra signatures will be added for cast()");
                castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { primitiveTypes[i].InstanceType, typeof(Type) }, FunctionDescription.BinaryCast));
            }

            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(Type) }, FunctionDescription.UnaryCast));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(object), typeof(Type) }, FunctionDescription.BinaryCast));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(ResourceType) }, FunctionDescription.UnaryCastResourceType));
            castSignatures.Add(new FunctionDescription(FunctionNameCast, new Type[] { typeof(object), typeof(ResourceType) }, FunctionDescription.BinaryCastResourceType));

            result.Add(FunctionNameCast, castSignatures.ToArray());

            #region Spatial Functions
            // geo.distance()
            var geoDistanceSignatures = new List<FunctionDescription>()
            { 
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Distance", typeof(GeographyPoint), typeof(GeographyPoint)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Distance", typeof(GeometryPoint), typeof(GeometryPoint)),
            };

            // spatial functions do not require null propagation
            foreach (var geoDistanceSignature in geoDistanceSignatures)
            {
                geoDistanceSignature.RequiresNullPropagation = false;
            }

#if !INTERNAL_DROP && !EFRTM
            geoDistanceSignatures.AddRange(ObjectContextSpatialUtil.GeoDistanceSignatures);
#endif
            result.Add("geo.distance", geoDistanceSignatures.ToArray());

            // geo.length()
            var geoLengthSignatures = new List<FunctionDescription>()
            { 
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Length", typeof(GeographyLineString)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Length", typeof(GeometryLineString)),
            };

            // spatial functions do not require null propagation
            foreach (var tmp in geoLengthSignatures)
            {
                tmp.RequiresNullPropagation = false;
            }

            result.Add("geo.length", geoLengthSignatures.ToArray());

            // geo.intersects()
            var geoIntersectsSignatures = new List<FunctionDescription>()
            { 
                CreateFunctionDescription(typeof(GeographyOperationsExtensions), false /* instance */, true /* method */, "Intersects", typeof(GeographyPoint), typeof(GeographyPolygon)),
                CreateFunctionDescription(typeof(GeometryOperationsExtensions), false /* instance */, true /* method */, "Intersects", typeof(GeometryPoint), typeof(GeometryPolygon)),
            };

            // spatial functions do not require null propagation
            foreach (var tmp in geoIntersectsSignatures)
            {
                tmp.RequiresNullPropagation = false;
            }

            result.Add("geo.intersects", geoIntersectsSignatures.ToArray());
            #endregion

            return result;
        }
        /// <summary>Creates a new function description for a method or property.</summary>
        /// <param name="targetType">Type on which property or method is declared.</param>
        /// <param name="instance">Whether an instance member is looked for.</param>
        /// <param name="method">Whether a method (rather than a property) is looked for.</param>
        /// <param name="name">Name of member.</param>
        /// <param name="parameterTypes">Parameter types.</param>
        /// <returns>A new function description.</returns>
        private static FunctionDescription CreateFunctionDescription(
            Type targetType,
            bool instance,
            bool method,
            string name,
            params Type[] parameterTypes)
        {
            Debug.Assert(targetType != null, "targetType != null");
            Debug.Assert(name != null, "name != null");
            Debug.Assert(parameterTypes.Length == 0 || method, "parameterTypes.Length == 0 || method");
            Debug.Assert(method || instance, "method || instance");

            BindingFlags flags = BindingFlags.Public | (instance ? BindingFlags.Instance : BindingFlags.Static);
            MemberInfo member;

            if (method)
            {
                member = targetType.GetMethod(name, flags, null, parameterTypes, null);
                Debug.Assert(member != null, "methodInfo != null");
            }
            else
            {
                member = targetType.GetProperty(name, flags);
                Debug.Assert(member != null, "propertyInfo != null");
            }

            Type[] functionParameterTypes;
            if (instance)
            {
                functionParameterTypes = new Type[parameterTypes.Length + 1];
                functionParameterTypes[0] = targetType;
                parameterTypes.CopyTo(functionParameterTypes, 1);
            }
            else
            {
                functionParameterTypes = parameterTypes;
            }

            FunctionDescription result = new FunctionDescription(member, functionParameterTypes);
            if (method)
            {
                if (instance)
                {
                    result.ConversionFunction = new Func<Expression, Expression[], Expression>(result.InstanceMethodConversionFunction);
                }
                else
                {
                    result.ConversionFunction = new Func<Expression, Expression[], Expression>(result.StaticMethodConversionFunction);
                }
            }
            else
            {
                Debug.Assert(instance, "instance");
                result.ConversionFunction = new Func<Expression, Expression[], Expression>(result.InstancePropertyConversionFunction);
            }

            return result;
        }
        /// <summary>Finds the best fitting function for the specified arguments.</summary>
        /// <param name="functions">Functions to consider.</param>
        /// <param name="arguments">Arguments; if a best function is found, promoted arguments.</param>
        /// <returns>The best fitting function; null if none found or ambiguous.</returns>
        internal FunctionDescription FindBestFunction(FunctionDescription[] functions, ref Expression[] arguments)
        {
            Debug.Assert(functions != null, "functions != null");
            List<FunctionDescription> applicableFunctions = new List<FunctionDescription>(functions.Length);
            List<Expression[]> applicablePromotedArguments = new List<Expression[]>(functions.Length);

            // Build a list of applicable functions (and cache their promoted arguments).
            foreach (FunctionDescription candidate in functions)
            {
                if (candidate.ParameterTypes.Length != arguments.Length)
                {
                    continue;
                }

                Expression[] promotedArguments = new Expression[arguments.Length];
                bool argumentsMatch = true;
                for (int i = 0; i < candidate.ParameterTypes.Length; i++)
                {
                    promotedArguments[i] = this.PromoteExpression(arguments[i], candidate.ParameterTypes[i], true);
                    if (promotedArguments[i] == null)
                    {
                        argumentsMatch = false;
                        break;
                    }
                }

                if (argumentsMatch)
                {
                    applicableFunctions.Add(candidate);
                    applicablePromotedArguments.Add(promotedArguments);
                }
            }

            // Return the best applicable function.
            if (applicableFunctions.Count == 0)
            {
                // No matching function.
                return null;
            }
            else if (applicableFunctions.Count == 1)
            {
                arguments = applicablePromotedArguments[0];
                return applicableFunctions[0];
            }
            else
            {
                // Find a single function which is better than all others.
                int bestFunctionIndex = -1;
                for (int i = 0; i < applicableFunctions.Count; i++)
                {
                    bool betterThanAllOthers = true;
                    for (int j = 0; j < applicableFunctions.Count; j++)
                    {
                        if (i != j && IsBetterThan(arguments, applicableFunctions[j].ParameterTypes, applicableFunctions[i].ParameterTypes))
                        {
                            betterThanAllOthers = false;
                            break;
                        }
                    }

                    if (betterThanAllOthers)
                    {
                        Debug.Assert(bestFunctionIndex == -1, "bestFunctionIndex == -1");
                        bestFunctionIndex = i;
                        break;
                    }
                }

                if (bestFunctionIndex == -1)
                {
                    return null;
                }

                arguments = applicablePromotedArguments[bestFunctionIndex];
                return applicableFunctions[bestFunctionIndex];
            }
        }