private static TExpression ConvertFromUnknownType <TExpression>(TExpression expression, Type type) where TExpression : ODataExpression { // sanity check Throw.If(expression.Type != ODataExpressionType.Unknown, "expression: must be of unknown type"); switch (expression.Kind) { case ODataExpressionKind.MemberAccess: var memberAccess = (ODataMemberAccessExpression)expression.As <ODataExpression>(); return((TExpression)MemberAccess(memberAccess.Expression, ODataEntity.GetProperty(memberAccess.Member.Name, type)).As <ODataExpression>()); default: throw Throw.UnexpectedCase(expression.Kind); } }
private ODataExpression ParseSimple() { if (this.TryEat(ODataTokenKind.LeftParen)) { // parse group var group = this.ParseExpression(); this.Eat(ODataTokenKind.RightParen); return(group); } ODataToken next; if (this.TryEat(ODataTokenKind.Identifier, out next)) { if (this.TryEat(ODataTokenKind.LeftParen)) { // parse function ODataFunction function; if (!Enum.TryParse(next.Text, ignoreCase: true, result: out function)) { throw new ODataParseException(next.Text + " is not a known ODataFunction!"); } var arguments = this.ParseExpressionList(this.ParseExpression, ODataTokenKind.Comma); this.Eat(ODataTokenKind.RightParen); if (function == ODataFunction.IsOf || function == ODataFunction.Cast) { var typeLiteral = this.ReParseAsType(arguments[arguments.Count - 1]); var argumentsCopy = arguments.ToArray(); argumentsCopy[arguments.Count - 1] = ODataExpression.Constant(typeLiteral); arguments = argumentsCopy; } return(ODataExpression.Call(function, arguments)); } // parse member access var type = this._elementType; // root element type ODataMemberAccessExpression access = null; // root member while (true) { // get the property var property = typeof(ODataObject).IsAssignableFrom(type) ? ODataEntity.GetProperty(next.Text, typeof(ODataObject)) : type.GetProperty(next.Text, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (property == null) { throw new ODataParseException("Property '" + next.Text + "' could not be found on type " + type.FullName); } access = ODataExpression.MemberAccess(access, property); // if we don't see '/' followed by an identifier, we're done // we can't just use TryEat() here, because we need to distinguish between Foo/Bar/* and Foo/Bar/Baz if (this.Next().Kind != ODataTokenKind.Slash || this.Next(2).Kind != ODataTokenKind.Identifier) { break; } // otherwise, update next to the next id and then advance type down the property chain this.Eat(ODataTokenKind.Slash); next = this.Eat(ODataTokenKind.Identifier); type = property.PropertyType; } return(access); } // literals if (this.TryEat(ODataTokenKind.NullLiteral, out next)) { return(ODataExpression.Constant(null)); } if (this.TryEat(ODataTokenKind.BinaryLiteral, out next)) { throw new NotImplementedException("Binary Literal Parse"); } if (this.TryEat(ODataTokenKind.BooleanLiteral, out next)) { return(ODataExpression.Constant(bool.Parse(next.Text))); } // see comment on the enum //if (this.TryEat(ODataTokenKind.ByteLiteral, out next)) //{ // return ODataExpression.Constant(Convert.ToByte(next.Text, fromBase: 16)); //} if (this.TryEat(ODataTokenKind.DateTimeLiteral, out next)) { Func <string, string> zeroIfEmpty = s => s.Length == 0 ? "0" : s; var dateTime = new DateTime( year: int.Parse(next.Match.Groups["year"].Value), month: int.Parse(next.Match.Groups["month"].Value), day: int.Parse(next.Match.Groups["day"].Value), hour: int.Parse(next.Match.Groups["hour"].Value), minute: int.Parse(next.Match.Groups["minute"].Value), second: int.Parse(zeroIfEmpty(next.Match.Groups["second"].Value)), millisecond: 0 ) .AddSeconds(double.Parse(zeroIfEmpty(next.Match.Groups["fraction"].Value))); return(ODataExpression.Constant(dateTime)); } if (this.TryEat(ODataTokenKind.DecimalLiteral, out next)) { return(ODataExpression.Constant(decimal.Parse(next.Text.Substring(0, next.Text.Length - 1)))); } if (this.TryEat(ODataTokenKind.DoubleLiteral, out next)) { return(ODataExpression.Constant(double.Parse(next.Text))); } if (this.TryEat(ODataTokenKind.SingleLiteral, out next)) { return(ODataExpression.Constant(float.Parse(next.Text.Substring(0, next.Text.Length - 1)))); } if (this.TryEat(ODataTokenKind.GuidLiteral, out next)) { return(ODataExpression.Constant(Guid.Parse(next.Match.Groups["digits"].Value))); } if (this.TryEat(ODataTokenKind.Int32Literal, out next)) { // Note: this will fail hard if we have an out-of-range int value. However, this is consistent // with MSFT's implementation (see http://services.odata.org/v3/odata/odata.svc/Products?$format=json&$filter=Price%20ge%202147483648) return(ODataExpression.Constant(int.Parse(next.Text))); } if (this.TryEat(ODataTokenKind.Int64Literal, out next)) { return(ODataExpression.Constant(long.Parse(next.Text.Substring(0, next.Text.Length - 1)))); } if (this.TryEat(ODataTokenKind.StringLiteral, out next)) { // unescaping, from http://stackoverflow.com/questions/3979367/how-to-escape-a-single-quote-to-be-used-in-an-odata-query return(ODataExpression.Constant(next.Match.Groups["chars"].Value.Replace("''", "'"))); } throw new ODataParseException("Unexpected token " + this.Next()); }