private Expression ProduceComparatorExpressionForSingleFieldCondition()

in WEB/Src/PerformanceCollector/PerformanceCollector/Filter.cs [325:597]


        private Expression ProduceComparatorExpressionForSingleFieldCondition(Expression fieldExpression, Type fieldType, bool isFieldTypeNullable = false)
        {
            // this must determine an appropriate runtime comparison given the field type, the predicate, and the comparand
            TypeCode fieldTypeCode = Type.GetTypeCode(fieldType);
            switch (fieldTypeCode)
            {
                case TypeCode.Boolean:
                    {
                        this.ThrowOnInvalidFilter(fieldType, !this.comparandBoolean.HasValue);

                        switch (this.predicate)
                        {
                            case Predicate.Equal:
                                // fieldValue == this.comparandBoolean.Value;
                                return Expression.Equal(fieldExpression, Expression.Constant(this.comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
                            case Predicate.NotEqual:
                                // fieldValue != this.comparandBoolean.Value;
                                return Expression.NotEqual(fieldExpression, Expression.Constant(this.comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
                            default:
                                this.ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }

                    break;
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                    {
                        if (fieldType.GetTypeInfo().IsEnum)
                        {
                            // this is actually an Enum
                            object enumValue = null;
                            try
                            {
                                enumValue = Enum.Parse(fieldType, this.comparand, true);
                            }
                            catch (Exception)
                            {
                                // we must throw unless this.predicate is either Contains or DoesNotContain, in which case it's ok
                                this.ThrowOnInvalidFilter(fieldType, this.predicate != Predicate.Contains && this.predicate != Predicate.DoesNotContain);
                            }

                            Type enumUnderlyingType = fieldType.GetTypeInfo().GetEnumUnderlyingType();

                            switch (this.predicate)
                            {
                                case Predicate.Equal:
                                    // fieldValue == enumValue
                                    return Expression.Equal(fieldExpression, Expression.Constant(enumValue, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(fieldType) : fieldType));
                                case Predicate.NotEqual:
                                    // fieldValue != enumValue
                                    return Expression.NotEqual(fieldExpression, Expression.Constant(enumValue, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(fieldType) : fieldType));
                                case Predicate.LessThan:
                                    // (int)fieldValue < (int)enumValue
                                    // (int?)fieldValue < (int?)enumValue
                                    Type underlyingType = isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.LessThan(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.GreaterThan:
                                    // (int)fieldValue > (int)enumValue
                                    // (int?)fieldValue > (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.GreaterThan(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.LessThanOrEqual:
                                    // (int)fieldValue <= (int)enumValue
                                    // (int?)fieldValue <= (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.LessThanOrEqual(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.GreaterThanOrEqual:
                                    // (int)fieldValue >= (int)enumValue
                                    // (int?)fieldValue >= (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.GreaterThanOrEqual(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.Contains:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
                                    Expression toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
                                    Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                                case Predicate.DoesNotContain:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
                                    toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
                                    indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.Equal(indexOfCall, Expression.Constant(-1));
                                default:
                                    this.ThrowOnInvalidFilter(fieldType);
                                    break;
                            }
                        }
                        else
                        {
                            // this is a regular numerical type
                            // in order for the expression to compile, we must cast to double unless it's already double
                            // we're using double as the lowest common denominator for all numerical types
                            Expression fieldConvertedExpression = fieldTypeCode == TypeCode.Double
                                                                      ? fieldExpression
                                                                      : Expression.ConvertChecked(fieldExpression, isFieldTypeNullable ? typeof(double?) : typeof(double));

                            switch (this.predicate)
                            {
                                case Predicate.Equal:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.Equal(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.NotEqual:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.NotEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.LessThan:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.LessThan(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.GreaterThan:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.GreaterThan(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.LessThanOrEqual:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.LessThanOrEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.GreaterThanOrEqual:
                                    this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                    return Expression.GreaterThanOrEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(this.comparandDouble.Value, isFieldTypeNullable ? typeof(Nullable<>).MakeGenericType(typeof(double)) : typeof(double)));
                                case Predicate.Contains:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
                                    Expression toStringCall = isFieldTypeNullable
                                                                  ? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
                                                                  : Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
                                    Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                                case Predicate.DoesNotContain:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
                                    toStringCall = isFieldTypeNullable
                                                       ? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
                                                       : Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
                                    indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.Equal(indexOfCall, Expression.Constant(-1));
                                default:
                                    this.ThrowOnInvalidFilter(fieldType);
                                    break;
                            }
                        }
                    }

                    break;
                case TypeCode.String:
                    {
                        Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), fieldExpression);

                        Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));

                        switch (this.predicate)
                        {
                            case Predicate.Equal:
                                // (fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                            case Predicate.NotEqual:
                                // !(fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
                            case Predicate.LessThan:
                            case Predicate.GreaterThan:
                            case Predicate.LessThanOrEqual:
                            case Predicate.GreaterThanOrEqual:
                                // double.TryParse(fieldValue, out temp) && temp {<, <=, >, >=} comparandDouble
                                this.ThrowOnInvalidFilter(fieldType, !this.comparandDouble.HasValue);
                                return this.CreateStringToDoubleComparisonBlock(fieldExpression, this.predicate);
                            case Predicate.Contains:
                                // fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
                                return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                            case Predicate.DoesNotContain:
                                // fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
                                return Expression.Equal(indexOfCall, Expression.Constant(-1));
                            default:
                                this.ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }

                    break;
                default:
                    Type nullableUnderlyingType;
                    if (fieldType == typeof(TimeSpan))
                    {
                        this.ThrowOnInvalidFilter(fieldType, !this.comparandTimeSpan.HasValue);

                        switch (this.predicate)
                        {
                            case Predicate.Equal:
                                Func<TimeSpan, bool> comparator = fieldValue => fieldValue == this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.NotEqual:
                                comparator = fieldValue => fieldValue != this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.LessThan:
                                comparator = fieldValue => fieldValue < this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.GreaterThan:
                                comparator = fieldValue => fieldValue > this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.LessThanOrEqual:
                                comparator = fieldValue => fieldValue <= this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.GreaterThanOrEqual:
                                comparator = fieldValue => fieldValue >= this.comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            default:
                                this.ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }
                    else if (fieldType == typeof(Uri))
                    {
                        Expression toStringCall = Expression.Call(fieldExpression, UriToStringMethodInfo);

                        Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), toStringCall);

                        Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));

                        switch (this.predicate)
                        {
                            case Predicate.Equal:
                                // (fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                            case Predicate.NotEqual:
                                // !(fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(this.comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
                            case Predicate.Contains:
                                // fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
                                return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                            case Predicate.DoesNotContain:
                                // fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
                                return Expression.Equal(indexOfCall, Expression.Constant(-1));
                            default:
                                this.ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }
                    else if ((nullableUnderlyingType = Nullable.GetUnderlyingType(fieldType)) != null)
                    {
                        // make a recursive call for the underlying type
                        return ProduceComparatorExpressionForSingleFieldCondition(fieldExpression, nullableUnderlyingType, true);
                    }
                    else
                    {
                        this.ThrowOnInvalidFilter(fieldType);
                    }

                    break;
            }

            return null;
        }