private bool NeedsParens(ODataExpression leftOrRight) { if (leftOrRight.Kind != ODataExpressionKind.BinaryOp) { return false; } var binaryOp = (ODataBinaryOpExpression)leftOrRight; switch (binaryOp.Operator) { case ODataBinaryOp.Or: return this.Operator > ODataBinaryOp.Or; case ODataBinaryOp.And: case ODataBinaryOp.Equal: case ODataBinaryOp.NotEqual: case ODataBinaryOp.GreaterThan: case ODataBinaryOp.GreaterThanOrEqual: case ODataBinaryOp.LessThan: case ODataBinaryOp.LessThanOrEqual: return this.Operator > ODataBinaryOp.And; case ODataBinaryOp.Add: case ODataBinaryOp.Subtract: case ODataBinaryOp.Multiply: case ODataBinaryOp.Divide: case ODataBinaryOp.Modulo: return this.Operator > ODataBinaryOp.Subtract; default: throw Throw.UnexpectedCase(binaryOp); } }
internal ODataBinaryOpExpression(ODataExpression left, ODataBinaryOp @operator, ODataExpression right, Type clrType) : base(ODataExpressionKind.BinaryOp, clrType.ToODataExpressionType(), clrType) { Throw<ArgumentException>.If(right.Type != left.Type, "right & left: must have equal types"); this.Right = right; this.Operator = @operator; this.Left = left; }
protected virtual void Visit(ODataExpression node) { if (node == null) { return; } switch (node.Kind) { case ODataExpressionKind.Convert: this.VisitConvert((ODataConvertExpression)node); break; case ODataExpressionKind.Constant: this.VisitConstant((ODataConstantExpression)node); break; case ODataExpressionKind.Call: this.VisitCall((ODataCallExpression)node); break; case ODataExpressionKind.BinaryOp: this.VisitBinaryOp((ODataBinaryOpExpression)node); break; case ODataExpressionKind.UnaryOp: this.VisitUnaryOp((ODataUnaryOpExpression)node); break; case ODataExpressionKind.MemberAccess: this.VisitMemberAccess((ODataMemberAccessExpression)node); break; case ODataExpressionKind.Query: this.VisitQuery((ODataQueryExpression)node); break; case ODataExpressionKind.SelectColumn: this.VisitSelectColumn((ODataSelectColumnExpression)node); break; case ODataExpressionKind.SortKey: this.VisitSortKey((ODataSortKeyExpression)node); break; default: throw new InvalidOperationException("Unexpected expression kind " + node.Kind); } }
internal ODataSortKeyExpression(ODataExpression expression, bool descending) : base(ODataExpressionKind.SortKey, expression.Type, expression.ClrType) { this.Expression = expression; this.Descending = descending; }
/// <summary> /// Attempts to translate the given <see cref="MemberExpression"/> as a special OData member, like <see cref="Nullable{T}.Value"/> or /// <see cref="string.Length"/>. /// </summary> private bool TryTranslateMemberAccessAsSpecialMember(MemberExpression memberAccess, out ODataExpression result) { // TODO FUTURE null handling? Func<IEnumerable<ODataExpression>> translateInstance = () => this._translator.TranslateInternal(memberAccess.Expression).Enumerate(); if (memberAccess.Member.DeclaringType == typeof(string) && memberAccess.Member.Name == "Length") { result = ODataExpression.Call(ODataFunction.Length, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Year") { result = ODataExpression.Call(ODataFunction.Year, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Month") { result = ODataExpression.Call(ODataFunction.Month, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Day") { result = ODataExpression.Call(ODataFunction.Day, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Hour") { result = ODataExpression.Call(ODataFunction.Hour, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Minute") { result = ODataExpression.Call(ODataFunction.Minute, translateInstance()); } else if (memberAccess.Member.DeclaringType == typeof(DateTime) && memberAccess.Member.Name == "Second") { result = ODataExpression.Call(ODataFunction.Second, translateInstance()); } // nullable properties else if (memberAccess.Member.Name == "HasValue" && memberAccess.Member.DeclaringType.IsGenericOfType(typeof(Nullable<>))) { // for HasValue we just re-translate expr != null result = this._translator.TranslateInternal(Expression.NotEqual(memberAccess.Expression, Expression.Constant(null, memberAccess.Expression.Type))); } else if (memberAccess.Member.Name == "Value" && memberAccess.Member.DeclaringType.IsGenericOfType(typeof(Nullable<>))) { // .Value calls can just be ignored, since OData doesn't have the notion of nullable types result = this._translator.TranslateInternal(memberAccess.Expression); } else { result = null; return false; } return true; }
// in general, unary ops don't change the type. If we ever implement one that does, we can change this logic internal ODataUnaryOpExpression(ODataExpression operand, ODataUnaryOp @operator) : base(ODataExpressionKind.UnaryOp, operand.Type, operand.ClrType) { this.Operand = operand; this.Operator = @operator; }
// TODO VNEXT expand internal ODataQueryExpression( ODataExpression filter, IReadOnlyList<ODataSortKeyExpression> orderBy, int? top, int skip, string format, ODataInlineCountOption inlineCount, IReadOnlyList<ODataSelectColumnExpression> select) : base(ODataExpressionKind.Query, ODataExpressionType.Complex, typeof(IQueryable)) { this.Filter = filter; this.OrderBy = orderBy; this.Top = top; this.Skip = skip; this.Format = format; this.InlineCount = inlineCount; this.Select = select; }
internal ODataQueryExpression Update(ODataExpression filter = null, IEnumerable<ODataSortKeyExpression> orderBy = null, int? top = -1, int? skip = null, string format = null, ODataInlineCountOption? inlineCount = null, IEnumerable<ODataSelectColumnExpression> select = null) { return Query( filter: filter ?? this.Filter, orderBy: orderBy.NullSafe(ob => ob.ToArray(), ifNullReturn: this.OrderBy), top: top == -1 ? this.Top : top, skip: skip ?? this.Skip, format: format ?? this.Format, inlineCount: inlineCount ?? this.InlineCount, select: select.NullSafe(sc => sc.ToArray(), ifNullReturn: this.Select) ); }
internal ODataConvertExpression(ODataExpression expression, Type clrType) : base(ODataExpressionKind.Convert, clrType.ToODataExpressionType(), clrType) { this.Expression = expression; }
internal static ODataQueryExpression Query( ODataExpression filter = null, IReadOnlyList<ODataSortKeyExpression> orderBy = null, int? top = null, int skip = 0, string format = null, ODataInlineCountOption inlineCount = ODataInlineCountOption.None, IReadOnlyList<ODataSelectColumnExpression> select = null) { Throw.If(filter != null && filter.Type != ODataExpressionType.Boolean, "filter: must be boolean-typed"); if (top.HasValue) { Throw.IfOutOfRange(top.Value, min: 0, paramName: "top"); } Throw.IfOutOfRange(skip, min: 0, paramName: "skip"); if (format != null) { Throw.If(string.IsNullOrWhiteSpace(format), "format: must not be empty or whitespace"); } return new ODataQueryExpression( filter: filter, orderBy: orderBy != null ? orderBy.ToImmutableList() : Empty<ODataSortKeyExpression>.Array, top: top, skip: skip, format: format, inlineCount: inlineCount, select: select != null ? select.ToImmutableList() : Empty<ODataSelectColumnExpression>.Array ); }
internal static ODataSortKeyExpression SortKey(ODataExpression expression, bool descending = false) { Throw.IfNull(expression, "expression"); return(new ODataSortKeyExpression(expression, descending: descending)); }
internal static ODataBinaryOpExpression BinaryOp(ODataExpression left, ODataBinaryOp @operator, ODataExpression right) { Throw.IfNull(left, "left"); Throw.IfNull(right, "right"); // add in implicit conversions as needed ODataExpression convertedLeft, convertedRight; if ((left.Type == ODataExpressionType.Unknown || left.Type == ODataExpressionType.Null) && (right.Type == ODataExpressionType.Unknown || right.Type == ODataExpressionType.Null)) { var canonicalType = GetCanonicalArgumentType(@operator); if (canonicalType != typeof(ODataObject)) { convertedLeft = left.Type == ODataExpressionType.Unknown ? ConvertFromUnknownType(left, canonicalType) : LiftNullToUnknown(left); convertedRight = right.Type == ODataExpressionType.Unknown ? ConvertFromUnknownType(right, canonicalType) : LiftNullToUnknown(right); } else { convertedLeft = left.Type == ODataExpressionType.Null ? LiftNullToUnknown(left) : left; convertedRight = right.Type == ODataExpressionType.Null ? LiftNullToUnknown(right) : right; } } else if (left.Type == ODataExpressionType.Unknown) { convertedLeft = ConvertFromUnknownType(left, right.ClrType); convertedRight = right; } else if (right.Type == ODataExpressionType.Unknown) { convertedLeft = left; convertedRight = ConvertFromUnknownType(right, left.ClrType); } else if (left.Type == right.Type) { convertedLeft = left; convertedRight = right; } else if (left.Type.IsImplicityCastableTo(right.Type)) { convertedLeft = Convert(left, right.ClrType); convertedRight = right; } else if (right.Type.IsImplicityCastableTo(left.Type)) { convertedLeft = left; convertedRight = Convert(right, left.ClrType); } else { throw new ArgumentException(string.Format("Operator {0} cannot be applied to operands of type '{1}' and '{2}'", @operator, left.Type, right.Type)); } // determine the result type var operandClrType = convertedLeft.ClrType; var operandType = convertedLeft.Type; Type resultClrType; switch (@operator) { case ODataBinaryOp.Add: case ODataBinaryOp.Subtract: case ODataBinaryOp.Multiply: case ODataBinaryOp.Divide: case ODataBinaryOp.Modulo: Throw.If(!operandType.IsNumeric(), "Operator requires numeric type"); resultClrType = operandClrType; break; case ODataBinaryOp.LessThan: case ODataBinaryOp.LessThanOrEqual: case ODataBinaryOp.GreaterThanOrEqual: case ODataBinaryOp.GreaterThan: Throw.If( !operandType.IsNumeric() && !NonNumericComparableTypes.Contains(operandType) && operandType != ODataExpressionType.Unknown, "Operator requires a type with comparison operators defined" ); resultClrType = typeof(bool); break; case ODataBinaryOp.And: case ODataBinaryOp.Or: Throw.If(operandClrType != typeof(bool), "Boolean operators require operands with a non-nullable boolean type"); resultClrType = typeof(bool); break; case ODataBinaryOp.Equal: case ODataBinaryOp.NotEqual: resultClrType = typeof(bool); break; default: throw Throw.UnexpectedCase(@operator); } // construct the expression return(new ODataBinaryOpExpression(convertedLeft, @operator, convertedRight, resultClrType)); }
internal static ODataBinaryOpExpression BinaryOp(ODataExpression left, ODataBinaryOp @operator, ODataExpression right) { Throw.IfNull(left, "left"); Throw.IfNull(right, "right"); // add in implicit conversions as needed ODataExpression convertedLeft, convertedRight; if (left.Type == right.Type) { convertedLeft = left; convertedRight = right; } else if (left.Type.IsImplicityCastableTo(right.Type)) { convertedLeft = Convert(left, right.ClrType); convertedRight = right; } else if (right.Type.IsImplicityCastableTo(left.Type)) { convertedLeft = left; convertedRight = Convert(right, left.ClrType); } else { throw new ArgumentException(string.Format("Types {0} and {1} cannot be used with operator {2}", left.Type, right.Type, @operator)); } // determine the result type var operandClrType = convertedLeft.ClrType; var operandType = convertedLeft.Type; Type resultClrType; switch (@operator) { case ODataBinaryOp.Add: case ODataBinaryOp.Subtract: case ODataBinaryOp.Multiply: case ODataBinaryOp.Divide: case ODataBinaryOp.Modulo: Throw.If(!operandType.IsNumeric(), "Operator requires numeric type"); resultClrType = operandClrType; break; case ODataBinaryOp.LessThan: case ODataBinaryOp.LessThanOrEqual: case ODataBinaryOp.GreaterThanOrEqual: case ODataBinaryOp.GreaterThan: Throw.If(!operandType.IsNumeric() && !NonNumericComparableTypes.Contains(operandType), "Operator requires a type with comparison operators defined"); resultClrType = typeof(bool); break; case ODataBinaryOp.And: case ODataBinaryOp.Or: Throw.If(operandClrType != typeof(bool), "Boolean operators require operands with a non-nullable boolean type"); resultClrType = typeof(bool); break; case ODataBinaryOp.Equal: case ODataBinaryOp.NotEqual: resultClrType = typeof(bool); break; default: throw Throw.UnexpectedCase(@operator); } // construct the expression return new ODataBinaryOpExpression(convertedLeft, @operator, convertedRight, resultClrType); }
internal static ODataUnaryOpExpression UnaryOp(ODataExpression operand, ODataUnaryOp @operator) { Throw.IfNull(operand, "operand"); Throw.If(operand.Type != ODataExpressionType.Boolean, "operand: must be a boolean type"); return new ODataUnaryOpExpression(operand, @operator); }
internal static ODataSortKeyExpression SortKey(ODataExpression expression, bool descending = false) { Throw.IfNull(expression, "expression"); return new ODataSortKeyExpression(expression, descending: descending); }
public ISet<MemberPath> FindPaths(ODataExpression node) { this._paths = new HashSet<MemberPath>(); this.Visit(node); return this._paths; }
internal ODataBinaryOpExpression(ODataExpression left, ODataBinaryOp @operator, ODataExpression right, Type clrType) : base(ODataExpressionKind.BinaryOp, clrType.ToODataExpressionType(), clrType) { Throw <ArgumentException> .If(right.Type != left.Type, "right & left: must have equal types"); this.Right = right; this.Operator = @operator; this.Left = left; }
internal static ODataConvertExpression Convert(ODataExpression expression, Type clrType) { Throw.IfNull(expression, "expression"); Throw.IfNull(clrType, "clrType"); Throw<ArgumentException>.If( // can only convert where (1) a cast is valid or (2) a cast might succeed because the target type // derives from of the expression type !expression.ClrType.IsCastableTo(clrType) && !clrType.IsAssignableFrom(expression.ClrType), () => "cannot convert from " + expression.ClrType + " to " + clrType ); return new ODataConvertExpression(expression, clrType); }