/// <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>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]; } }