/// <summary>Attempts to parse key values from the specified text.</summary>
        /// <param name='text'>Text to parse (not null).</param>
        /// <param name="allowNamedValues">Set to true if the parser should accept named values
        ///   so syntax like Name='value'. If this is false, the parsing will fail on such constructs.</param>
        /// <param name="allowNull">Set to true if the parser should accept null values.
        ///   If set to false, the parser will fail on null values.</param>
        /// <param name='instance'>After invocation, the parsed key instance.</param>
        /// <returns>
        /// true if the key instance was parsed; false if there was a 
        /// syntactic error.
        /// </returns>
        /// <remarks>
        /// The returned instance contains only string values. To get typed values, a call to
        /// <see cref="TryConvertValues"/> is necessary.
        /// </remarks>
        private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance)
        {
            Debug.Assert(text != null, "text != null");

            Dictionary<string, object> namedValues = null;
            List<object> positionalValues = null;

            ExpressionLexer lexer = new ExpressionLexer(text);
            Token currentToken = lexer.CurrentToken;
            if (currentToken.Id == TokenId.End)
            {
                instance = Empty;
                return true;
            }

            instance = null;
            do
            {
                if (currentToken.Id == TokenId.Identifier && allowNamedValues)
                {
                    // Name-value pair.
                    if (positionalValues != null)
                    {
                        // We cannot mix named and non-named values.
                        return false;
                    }

                    string identifier = lexer.CurrentToken.GetIdentifier();
                    lexer.NextToken();
                    if (lexer.CurrentToken.Id != TokenId.Equal)
                    {
                        return false;
                    }

                    lexer.NextToken();
                    if (!lexer.CurrentToken.IsKeyValueToken)
                    {
                        return false;
                    }

                    string namedValue = lexer.CurrentToken.Text;
                    WebUtil.CreateIfNull(ref namedValues);
                    if (namedValues.ContainsKey(identifier))
                    {
                        // Duplicate name.
                        return false;
                    }

                    namedValues.Add(identifier, namedValue);
                }
                else if (currentToken.IsKeyValueToken || (allowNull && currentToken.Id == TokenId.NullLiteral))
                {
                    // Positional value.
                    if (namedValues != null)
                    {
                        // We cannot mix named and non-named values.
                        return false;
                    }

                    WebUtil.CreateIfNull(ref positionalValues);
                    positionalValues.Add(lexer.CurrentToken.Text);
                }
                else
                {
                    return false;
                }

                // Read the next token. We should be at the end, or find
                // we have a comma followed by something.
                lexer.NextToken();
                currentToken = lexer.CurrentToken;
                if (currentToken.Id == TokenId.Comma)
                {
                    lexer.NextToken();
                    currentToken = lexer.CurrentToken;
                    if (currentToken.Id == TokenId.End)
                    {
                        // Trailing comma.
                        return false;
                    }
                }
            }
            while (currentToken.Id != TokenId.End);

            instance = new KeyInstance(namedValues, positionalValues);
            return true;
        }
            /// <summary>Initializes a new <see cref="ExpressionParser"/>.</summary>
            /// <param name="service">Service with data and configuration.</param>
            /// <param name="setForIt">Resource set for "it" contextual variable</param>
            /// <param name="typeForIt">Type for "it" contextual variable</param>
            /// <param name="parameterForIt">Parameters for the current "it" context.</param>
            /// <param name="expression">Expression to parse.</param>
            internal ExpressionParser(IDataService service, ResourceSetWrapper setForIt, ResourceType typeForIt, ParameterExpression parameterForIt, string expression)
            {
                Debug.Assert(service != null, "service != null");

                // For Open types, we could potentially have typeForIt parameter set to null value,
                // However, we have decided not support ordering on Open properties which also implies
                // that we can not have entity typed properties on Open types 
                Debug.Assert(typeForIt != null, "typeForIt != null");

                Debug.Assert(expression != null, "expression != null");
                Debug.Assert(parameterForIt != null, "parameterForIt != null");

                this.service = service;
                this.provider = service.Provider;
                this.nullPropagationRequired = this.provider.NullPropagationRequired;
                this.literals = new Dictionary<Expression, string>(ReferenceEqualityComparer<Expression>.Instance);
                this.setForIt = setForIt;
                this.typeForIt = typeForIt;
                this.it = parameterForIt;
                this.lexer = new ExpressionLexer(expression);
            }
            /// <summary>Parse one of the literals of skip token.</summary>
            /// <param name="literal">Input literal.</param>
            /// <returns>Object resulting from conversion of literal.</returns>
            internal object ParseSkipTokenLiteral(String literal)
            {
                ExpressionLexer l = new ExpressionLexer(literal);
                Expression e = this.ParsePrimaryStart(l);
                if (e.NodeType != ExpressionType.Constant)
                {
                    throw new InvalidOperationException(Strings.RequsetQueryParser_ExpectingLiteralInSkipToken(literal));
                }

                return ((ConstantExpression)e).Value;
            }
 /// <summary>Handles the start of primary expressions.</summary>
 /// <param name="l">Lexer to use for reading tokens</param>
 /// <returns>The parsed expression.</returns>
 private Expression ParsePrimaryStart(ExpressionLexer l)
 {
     switch (l.CurrentToken.Id)
     {
         case TokenId.BooleanLiteral:
             return this.ParseTypedLiteral(typeof(bool), XmlConstants.EdmBooleanTypeName, l);
         case TokenId.DateTimeLiteral:
             return this.ParseTypedLiteral(typeof(DateTime), XmlConstants.EdmDateTimeTypeName, l);
         case TokenId.DecimalLiteral:
             return this.ParseTypedLiteral(typeof(decimal), XmlConstants.EdmDecimalTypeName, l);
         case TokenId.NullLiteral:
             return ExpressionParser.ParseNullLiteral(l);
         case TokenId.Identifier:
             Debug.Assert(Object.ReferenceEquals(this.lexer, l), "Lexer should be the member lexer");
             return this.ParseIdentifier();
         case TokenId.StringLiteral:
             return this.ParseTypedLiteral(typeof(string), XmlConstants.EdmStringTypeName, l);
         case TokenId.Int64Literal:
             return this.ParseTypedLiteral(typeof(Int64), XmlConstants.EdmInt64TypeName, l);
         case TokenId.IntegerLiteral:
             return this.ParseTypedLiteral(typeof(Int32), XmlConstants.EdmInt32TypeName, l);
         case TokenId.DoubleLiteral:
             return this.ParseTypedLiteral(typeof(double), XmlConstants.EdmDoubleTypeName, l);
         case TokenId.SingleLiteral:
             return this.ParseTypedLiteral(typeof(Single), XmlConstants.EdmSingleTypeName, l);
         case TokenId.GuidLiteral:
             return this.ParseTypedLiteral(typeof(Guid), XmlConstants.EdmGuidTypeName, l);
         case TokenId.BinaryLiteral:
             return this.ParseTypedLiteral(typeof(byte[]), XmlConstants.EdmBinaryTypeName, l);
         case TokenId.OpenParen:
             Debug.Assert(Object.ReferenceEquals(this.lexer, l), "Lexer should be the member lexer");
             return this.ParseParenExpression();
         default:
             throw ParseError(Strings.RequestQueryParser_ExpressionExpected(l.CurrentToken.Position));
     }
 }
            /// <summary>Handles typed literals.</summary>
            /// <param name="targetType">Expected type to be parsed.</param>
            /// <param name="targetTypeName">Expected type name.</param>
            /// <param name="l">Lexer to use for reading tokens</param>
            /// <returns>The constants expression produced by building the given literal.</returns>
            private Expression ParseTypedLiteral(Type targetType, string targetTypeName, ExpressionLexer l)
            {
                object targetValue;
                if (!WebConvert.TryKeyStringToPrimitive(l.CurrentToken.Text, targetType, out targetValue))
                {
                    string message = Strings.RequestQueryParser_UnrecognizedLiteral(targetTypeName, l.CurrentToken.Text, l.CurrentToken.Position);
                    throw ParseError(message);
                }

                Expression result = this.CreateLiteral(targetValue, l.CurrentToken.Text);
                l.NextToken();
                return result;
            }
            /// <summary>
            /// Generates a comparison expression which can handle NULL values for any type.
            /// NULL is always treated as the smallest possible value.
            /// So for example for strings NULL is smaller than any non-NULL string.
            /// For now only GreaterThan and LessThan operators are supported by this method.
            /// </summary>
            /// <param name="left">Left hand side expression</param>
            /// <param name="rightLiteral">Literal for the right hand side</param>
            /// <param name="op">gt or lt operator token</param>
            /// <returns>Resulting comparison expression (has a Boolean value)</returns>
            private Expression GenerateNullAwareComparison(Expression left, String rightLiteral, Token op)
            {
                Debug.Assert(
                    op.Text == ExpressionConstants.KeywordGreaterThan || op.Text == ExpressionConstants.KeywordLessThan,
                    "Only GreaterThan or LessThan operators are supported by the GenerateNullAwateComparison method for now.");

                ExpressionLexer l = new ExpressionLexer(rightLiteral);
                Expression right = this.ParsePrimaryStart(l);

                if (WebUtil.TypeAllowsNull(left.Type))
                {
                    if (!WebUtil.TypeAllowsNull(right.Type))
                    {
                        right = Expression.Convert(right, typeof(Nullable<>).MakeGenericType(right.Type));
                    }
                }
                else if (WebUtil.TypeAllowsNull(right.Type))
                {
                    left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type));
                }
                else
                {
                    // Can't perform NULL aware comparison on this type. Just let the normal 
                    // comparison deal with it. Since the type doesn't allow NULL one should 
                    // never appear, so normal comparison is just fine.
                    return this.GenerateComparisonExpression(left, right, op);
                }

                switch (op.Text)
                {
                    case ExpressionConstants.KeywordGreaterThan:
                        // (left != null) && ((right == null) || Compare(left, right) > 0)
                        if (left == WebUtil.NullLiteral)
                        {
                            return Expression.Constant(false, typeof(bool));
                        }
                        else if (right == WebUtil.NullLiteral)
                        {
                            return GenerateNotEqual(left, Expression.Constant(null, left.Type));
                        }
                        else
                        {
                            return GenerateLogicalAnd(
                                        GenerateNotEqual(left, Expression.Constant(null, left.Type)),
                                        GenerateLogicalOr(
                                            GenerateEqual(right, Expression.Constant(null, right.Type)),
                                            this.GenerateComparisonExpression(left, right, op)));
                        }

                    case ExpressionConstants.KeywordLessThan:
                        // (right != null) && ((left == null) || Compare(left, right) < 0)
                        if (right == WebUtil.NullLiteral)
                        {
                            return Expression.Constant(false, typeof(bool));
                        }
                        else if (left == WebUtil.NullLiteral)
                        {
                            return GenerateNotEqual(right, Expression.Constant(null, right.Type));
                        }
                        else
                        {
                            return GenerateLogicalAnd(
                                        GenerateNotEqual(right, Expression.Constant(null, left.Type)),
                                        GenerateLogicalOr(
                                            GenerateEqual(left, Expression.Constant(null, right.Type)),
                                            this.GenerateComparisonExpression(left, right, op)));
                        }

                    default:
                        // For now only < and > are supported as we use this only from $skiptoken
                        throw ParseError(
                            Strings.RequestQueryParser_NullOperatorUnsupported(op.Text, op.Position, this.lexer.ExpressionText));
                }
            }
 /// <summary>
 /// Generates a comparison expression given a left hand side expression and literal for right hand side
 /// </summary>
 /// <param name="left">Left hand side experssions</param>
 /// <param name="rightLiteral">Literal for right hand side</param>
 /// <param name="op">gt, eq or lt operator token</param>
 /// <returns>Resulting comparison expression</returns>
 private Expression GenerateComparison(Expression left, String rightLiteral, Token op)
 {
     ExpressionLexer l = new ExpressionLexer(rightLiteral);
     return this.GenerateComparisonExpression(left, this.ParsePrimaryStart(l), op);
 }
 /// <summary>Handles 'null' literals.</summary>
 /// <param name="l">Lexer to use for reading tokens</param>
 /// <returns>The parsed expression.</returns>
 private static Expression ParseNullLiteral(ExpressionLexer l)
 {
     Debug.Assert(l.CurrentToken.Id == TokenId.NullLiteral, "l.CurrentToken.Id == TokenId.NullLiteral");
     l.NextToken();
     return WebUtil.NullLiteral;
 }
Example #9
0
 private static List<List<string>> ReadExpandOrSelect(string value, bool select, IDataService dataService)
 {
     List<List<string>> list = new List<List<string>>();
     List<string> list2 = null;
     ExpressionLexer lexer = new ExpressionLexer(value);
     while (lexer.CurrentToken.Id != TokenId.End)
     {
         string text;
         bool flag = false;
         if (select && (lexer.CurrentToken.Id == TokenId.Star))
         {
             text = lexer.CurrentToken.Text;
             lexer.NextToken();
             flag = true;
         }
         else if (select)
         {
             bool flag2;
             text = lexer.ReadDottedIdentifier(true);
             if (dataService.Provider.GetNameFromContainerQualifiedName(text, out flag2) == "*")
             {
                 flag = true;
             }
         }
         else
         {
             text = lexer.ReadDottedIdentifier(false);
         }
         if (list2 == null)
         {
             list2 = new List<string> {
                 
             };
         }
         list2.Add(text);
         TokenId id = lexer.CurrentToken.Id;
         if (id != TokenId.End)
         {
             if (flag || (id != TokenId.Slash))
             {
                 lexer.ValidateToken(TokenId.Comma);
                 list2 = null;
             }
             lexer.NextToken();
         }
     }
     return list;
 }
Example #10
0
 private static bool TryParseFromUri(string text, bool allowNamedValues, bool allowNull, out KeyInstance instance)
 {
     Dictionary<string, object> dictionary = null;
     List<object> list = null;
     ExpressionLexer lexer = new ExpressionLexer(text);
     Token currentToken = lexer.CurrentToken;
     if (currentToken.Id == TokenId.End)
     {
         instance = Empty;
         return true;
     }
     instance = null;
     do
     {
         if ((currentToken.Id == TokenId.Identifier) && allowNamedValues)
         {
             if (list != null)
             {
                 return false;
             }
             string identifier = lexer.CurrentToken.GetIdentifier();
             lexer.NextToken();
             if (lexer.CurrentToken.Id != TokenId.Equal)
             {
                 return false;
             }
             lexer.NextToken();
             if (!lexer.CurrentToken.IsKeyValueToken)
             {
                 return false;
             }
             string str2 = lexer.CurrentToken.Text;
             WebUtil.CreateIfNull<Dictionary<string, object>>(ref dictionary);
             if (dictionary.ContainsKey(identifier))
             {
                 return false;
             }
             dictionary.Add(identifier, str2);
         }
         else
         {
             if (!currentToken.IsKeyValueToken && (!allowNull || (currentToken.Id != TokenId.NullLiteral)))
             {
                 return false;
             }
             if (dictionary != null)
             {
                 return false;
             }
             WebUtil.CreateIfNull<List<object>>(ref list);
             list.Add(lexer.CurrentToken.Text);
         }
         lexer.NextToken();
         currentToken = lexer.CurrentToken;
         if (currentToken.Id == TokenId.Comma)
         {
             lexer.NextToken();
             currentToken = lexer.CurrentToken;
             if (currentToken.Id == TokenId.End)
             {
                 return false;
             }
         }
     }
     while (currentToken.Id != TokenId.End);
     instance = new KeyInstance(dictionary, list);
     return true;
 }