/// <summary>
        /// Parses null literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseNullLiteral(ExpressionLexer lexer)
        {
            Debug.Assert(lexer != null, "lexer != null");
            Debug.Assert(lexer.CurrentToken.Kind == ExpressionTokenKind.NullLiteral, "this.lexer.CurrentToken.InternalKind == ExpressionTokenKind.NullLiteral");

            LiteralToken result = new LiteralToken(null, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
        /// <summary>
        /// Binds a literal value to a ConstantNode
        /// </summary>
        /// <param name="literalToken">Literal token to bind.</param>
        /// <returns>Bound query node.</returns>
        internal static QueryNode BindLiteral(LiteralToken literalToken)
        {
            ExceptionUtils.CheckArgumentNotNull(literalToken, "literalToken");

            if (!string.IsNullOrEmpty(literalToken.OriginalText))
            {
                if (literalToken.ExpectedEdmTypeReference != null)
                {
                    return(new ConstantNode(literalToken.Value, literalToken.OriginalText, literalToken.ExpectedEdmTypeReference));
                }

                return(new ConstantNode(literalToken.Value, literalToken.OriginalText));
            }

            return(new ConstantNode(literalToken.Value));
        }
        /// <summary>
        /// Parses a literal.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <returns>The literal query token or null if something else was found.</returns>
        internal static LiteralToken TryParseLiteral(ExpressionLexer lexer)
        {
            Debug.Assert(lexer != null, "lexer != null");

            switch (lexer.CurrentToken.Kind)
            {
            case ExpressionTokenKind.BooleanLiteral:
            case ExpressionTokenKind.DateLiteral:
            case ExpressionTokenKind.DecimalLiteral:
            case ExpressionTokenKind.StringLiteral:
            case ExpressionTokenKind.Int64Literal:
            case ExpressionTokenKind.IntegerLiteral:
            case ExpressionTokenKind.DoubleLiteral:
            case ExpressionTokenKind.SingleLiteral:
            case ExpressionTokenKind.GuidLiteral:
            case ExpressionTokenKind.BinaryLiteral:
            case ExpressionTokenKind.GeographyLiteral:
            case ExpressionTokenKind.GeometryLiteral:
            case ExpressionTokenKind.QuotedLiteral:
            case ExpressionTokenKind.DurationLiteral:
            case ExpressionTokenKind.TimeOfDayLiteral:
            case ExpressionTokenKind.DateTimeOffsetLiteral:
            case ExpressionTokenKind.CustomTypeLiteral:
                IEdmTypeReference literalEdmTypeReference = lexer.CurrentToken.GetLiteralEdmTypeReference();

                // Why not using EdmTypeReference.FullName? (literalEdmTypeReference.FullName)
                string edmConstantName = GetEdmConstantNames(literalEdmTypeReference);
                return(ParseTypedLiteral(lexer, literalEdmTypeReference, edmConstantName));

            case ExpressionTokenKind.BracedExpression:
            case ExpressionTokenKind.BracketedExpression:
            case ExpressionTokenKind.ParenthesesExpression:
            {
                LiteralToken result = new LiteralToken(lexer.CurrentToken.Text, lexer.CurrentToken.Text);
                lexer.NextToken();
                return(result);
            }

            case ExpressionTokenKind.NullLiteral:
                return(ParseNullLiteral(lexer));

            default:
                return(null);
            }
        }
Exemple #4
0
        /// <summary>
        /// Retrieve CollectionNode bound with given query token.
        /// </summary>
        /// <param name="queryToken">The query token</param>
        /// <param name="expectedType">The expected type that this collection holds</param>
        /// <param name="model">The Edm model</param>
        /// <returns>The corresponding CollectionNode</returns>
        private CollectionNode GetCollectionOperandFromToken(QueryToken queryToken, IEdmTypeReference expectedType, IEdmModel model)
        {
            CollectionNode operand      = null;
            LiteralToken   literalToken = queryToken as LiteralToken;

            if (literalToken != null)
            {
                string originalLiteralText = literalToken.OriginalText;

                // Parentheses-based collections are not standard JSON but bracket-based ones are.
                // Temporarily switch our collection to bracket-based so that the JSON reader will
                // correctly parse the collection. Then pass the original literal text to the token.
                string bracketLiteralText = originalLiteralText;
                if (bracketLiteralText[0] == '(')
                {
                    Debug.Assert(bracketLiteralText[bracketLiteralText.Length - 1] == ')',
                                 "Collection with opening '(' should have corresponding ')'");

                    StringBuilder replacedText = new StringBuilder(bracketLiteralText);
                    replacedText[0] = '[';
                    replacedText[replacedText.Length - 1] = ']';
                    bracketLiteralText = replacedText.ToString();
                }

                object       collection             = ODataUriConversionUtils.ConvertFromCollectionValue(bracketLiteralText, model, expectedType);
                LiteralToken collectionLiteralToken = new LiteralToken(collection, originalLiteralText, expectedType);
                operand = this.bindMethod(collectionLiteralToken) as CollectionConstantNode;
            }
            else
            {
                operand = this.bindMethod(queryToken) as CollectionNode;
            }

            if (operand == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_RightOperandNotCollectionValue);
            }

            return(operand);
        }
        /// <summary>
        /// Parses typed literals.
        /// </summary>
        /// <param name="lexer">The lexer to use.</param>
        /// <param name="targetTypeReference">Expected type to be parsed.</param>
        /// <param name="targetTypeName">The EDM type name of the expected type to be parsed.</param>
        /// <returns>The literal token produced by building the given literal.</returns>
        private static LiteralToken ParseTypedLiteral(ExpressionLexer lexer, IEdmTypeReference targetTypeReference, string targetTypeName)
        {
            Debug.Assert(lexer != null, "lexer != null");

            UriLiteralParsingException typeParsingException;
            object targetValue = DefaultUriLiteralParser.Instance.ParseUriStringToType(lexer.CurrentToken.Text, targetTypeReference, out typeParsingException);

            if (targetValue == null)
            {
                string message;

                if (typeParsingException == null)
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteral(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText);

                    throw ParseError(message);
                }
                else
                {
                    message = ODataErrorStrings.UriQueryExpressionParser_UnrecognizedLiteralWithReason(
                        targetTypeName,
                        lexer.CurrentToken.Text,
                        lexer.CurrentToken.Position,
                        lexer.ExpressionText,
                        typeParsingException.Message);

                    throw ParseError(message, typeParsingException);
                }
            }

            LiteralToken result = new LiteralToken(targetValue, lexer.CurrentToken.Text);

            lexer.NextToken();
            return(result);
        }
        /// <summary>
        /// Parse the complex/collection value in parameter alias.
        /// </summary>
        /// <param name="queryToken">The parsed token.</param>
        /// <param name="parameterType">The expected parameter type.</param>
        /// <param name="model">The model</param>
        /// <returns>Token with complex/collection value passed.</returns>
        private static QueryToken ParseComplexOrCollectionAlias(QueryToken queryToken, IEdmTypeReference parameterType, IEdmModel model)
        {
            LiteralToken valueToken = queryToken as LiteralToken;
            string       valueStr;

            if (valueToken != null && (valueStr = valueToken.Value as string) != null && !string.IsNullOrEmpty(valueToken.OriginalText))
            {
                var lexer = new ExpressionLexer(valueToken.OriginalText, true /*moveToFirstToken*/, false /*useSemicolonDelimiter*/, true /*parsingFunctionParameters*/);
                if (lexer.CurrentToken.Kind == ExpressionTokenKind.BracketedExpression || lexer.CurrentToken.Kind == ExpressionTokenKind.BracedExpression)
                {
                    object result = valueStr;
                    if (!parameterType.IsStructured() && !parameterType.IsStructuredCollectionType())
                    {
                        result = ODataUriUtils.ConvertFromUriLiteral(valueStr, ODataVersion.V4, model, parameterType);
                    }

                    // For non-primitive type, we have to pass parameterType to LiteralToken, then to ConstantNode so the service can know what the type it is.
                    return(new LiteralToken(result, valueToken.OriginalText, parameterType));
                }
            }

            return(queryToken);
        }
Exemple #7
0
        /// <summary>
        /// This is temp work around for $filter $orderby parameter expression which contains complex or collection
        ///     like "Fully.Qualified.Namespace.CanMoveToAddresses(addresses=[{\"Street\":\"NE 24th St.\",\"City\":\"Redmond\"},{\"Street\":\"Pine St.\",\"City\":\"Seattle\"}])";
        /// TODO:  $filter $orderby parameter expression which contains nested complex or collection should NOT be supported in this way
        ///     but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p));
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="operation">IEdmFunction or IEdmOperation</param>
        /// <param name="parameterTokens">The tokens to bind.</param>
        /// <param name="enableCaseInsensitive">Whether to enable case-insensitive when resolving parameter name.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param>
        /// <returns>The FunctionParameterTokens with complex or collection values converted from string like "{...}", or "[..,..,..]".</returns>
        private static ICollection <FunctionParameterToken> HandleComplexOrCollectionParameterValueIfExists(IEdmModel model, IEdmOperation operation, ICollection <FunctionParameterToken> parameterTokens, bool enableCaseInsensitive, bool enableUriTemplateParsing = false)
        {
            ICollection <FunctionParameterToken> partiallyParsedParametersWithComplexOrCollection = new Collection <FunctionParameterToken>();

            foreach (FunctionParameterToken paraToken in parameterTokens)
            {
                FunctionParameterToken funcParaToken;
                IEdmOperationParameter functionParameter = operation.FindParameter(paraToken.ParameterName);
                if (enableCaseInsensitive && functionParameter == null)
                {
                    functionParameter = ODataUriResolver.ResolveOperationParameterNameCaseInsensitive(operation, paraToken.ParameterName);

                    // The functionParameter can not be null here, else this method won't be called.
                    funcParaToken = new FunctionParameterToken(functionParameter.Name, paraToken.ValueToken);
                }
                else
                {
                    funcParaToken = paraToken;
                }

                FunctionParameterAliasToken aliasToken = funcParaToken.ValueToken as FunctionParameterAliasToken;
                if (aliasToken != null)
                {
                    aliasToken.ExpectedParameterType = functionParameter.Type;
                }

                LiteralToken valueToken = funcParaToken.ValueToken as LiteralToken;
                string       valueStr   = null;
                if (valueToken != null && (valueStr = valueToken.Value as string) != null && !string.IsNullOrEmpty(valueToken.OriginalText))
                {
                    ExpressionLexer lexer = new ExpressionLexer(valueToken.OriginalText, true /*moveToFirstToken*/, false /*useSemicolonDelimiter*/, true /*parsingFunctionParameters*/);
                    if (lexer.CurrentToken.Kind == ExpressionTokenKind.BracketedExpression || lexer.CurrentToken.Kind == ExpressionTokenKind.BracedExpression)
                    {
                        object result;
                        UriTemplateExpression expression;

                        if (enableUriTemplateParsing && UriTemplateParser.TryParseLiteral(lexer.CurrentToken.Text, functionParameter.Type, out expression))
                        {
                            result = expression;
                        }
                        else if (!functionParameter.Type.IsStructured() && !functionParameter.Type.IsStructuredCollectionType())
                        {
                            // ExpressionTokenKind.BracketedExpression means text like [1,2]
                            // so now try convert it to collection type value:
                            result = ODataUriUtils.ConvertFromUriLiteral(valueStr, ODataVersion.V4, model, functionParameter.Type);
                        }
                        else
                        {
                            // For complex & colleciton of complex directly return the raw string.
                            partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken);
                            continue;
                        }

                        LiteralToken           newValueToken    = new LiteralToken(result, valueToken.OriginalText);
                        FunctionParameterToken newFuncParaToken = new FunctionParameterToken(funcParaToken.ParameterName, newValueToken);
                        partiallyParsedParametersWithComplexOrCollection.Add(newFuncParaToken);
                        continue;
                    }
                }

                partiallyParsedParametersWithComplexOrCollection.Add(funcParaToken);
            }

            return(partiallyParsedParametersWithComplexOrCollection);
        }
Exemple #8
0
        /// <summary>Attempts to parse key values from the specified text.</summary>
        /// <param name='text'>Text to parse (not null).</param>
        /// <param name='instance'>After invocation, the parsed key instance.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</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
        /// TryConvertValues is necessary.
        /// </remarks>
        private static bool TryParseFromUri(string text, out SegmentArgumentParser instance, bool enableUriTemplateParsing)
        {
            Debug.Assert(text != null, "text != null");

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

            // parse keys just like function parameters
            ExpressionLexer          lexer      = new ExpressionLexer(string.Concat("(", text, ")"), true, false);
            UriQueryExpressionParser exprParser = new UriQueryExpressionParser(ODataUriParserSettings.DefaultFilterLimit /* default limit for parsing key value */, lexer);
            var tmp = (new FunctionCallParser(lexer, exprParser)).ParseArgumentListOrEntityKeyList();

            if (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                instance = null;
                return(false);
            }

            if (tmp.Length == 0)
            {
                instance = Empty;
                return(true);
            }

            foreach (FunctionParameterToken t in tmp)
            {
                string       valueText    = null;
                LiteralToken literalToken = t.ValueToken as LiteralToken;
                if (literalToken != null)
                {
                    valueText = literalToken.OriginalText;

                    // disallow "{...}" if enableUriTemplateParsing is false (which could have been seen as valid function parameter, e.g. array notation)
                    if (!enableUriTemplateParsing && UriTemplateParser.IsValidTemplateLiteral(valueText))
                    {
                        instance = null;
                        return(false);
                    }
                }
                else
                {
                    DottedIdentifierToken dottedIdentifierToken = t.ValueToken as DottedIdentifierToken; // for enum
                    if (dottedIdentifierToken != null)
                    {
                        valueText = dottedIdentifierToken.Identifier;
                    }
                }

                if (valueText != null)
                {
                    if (t.ParameterName == null)
                    {
                        if (namedValues != null)
                        {
                            instance = null; // We cannot mix named and non-named values.
                            return(false);
                        }

                        CreateIfNull(ref positionalValues);
                        positionalValues.Add(valueText);
                    }
                    else
                    {
                        if (positionalValues != null)
                        {
                            instance = null; // We cannot mix named and non-named values.
                            return(false);
                        }

                        CreateIfNull(ref namedValues);
                        namedValues.Add(t.ParameterName, valueText);
                    }
                }
                else
                {
                    instance = null;
                    return(false);
                }
            }

            instance = new SegmentArgumentParser(namedValues, positionalValues, false, enableUriTemplateParsing);
            return(true);
        }
Exemple #9
0
        /// <summary>
        /// Retrieve CollectionNode bound with given query token.
        /// </summary>
        /// <param name="queryToken">The query token</param>
        /// <param name="expectedType">The expected type that this collection holds</param>
        /// <param name="model">The Edm model</param>
        /// <returns>The corresponding CollectionNode</returns>
        private CollectionNode GetCollectionOperandFromToken(QueryToken queryToken, IEdmTypeReference expectedType, IEdmModel model)
        {
            CollectionNode operand      = null;
            LiteralToken   literalToken = queryToken as LiteralToken;

            if (literalToken != null)
            {
                string originalLiteralText = literalToken.OriginalText;

                // Parentheses-based collections are not standard JSON but bracket-based ones are.
                // Temporarily switch our collection to bracket-based so that the JSON reader will
                // correctly parse the collection. Then pass the original literal text to the token.
                string bracketLiteralText = originalLiteralText;
                if (bracketLiteralText[0] == '(')
                {
                    Debug.Assert(bracketLiteralText[bracketLiteralText.Length - 1] == ')',
                                 "Collection with opening '(' should have corresponding ')'");

                    StringBuilder replacedText = new StringBuilder(bracketLiteralText);
                    replacedText[0] = '[';
                    replacedText[replacedText.Length - 1] = ']';
                    bracketLiteralText = replacedText.ToString();

                    Debug.Assert(expectedType.IsCollection());
                    string expectedTypeFullName = expectedType.Definition.AsElementType().FullTypeName();
                    if (expectedTypeFullName.Equals("Edm.String", StringComparison.Ordinal))
                    {
                        // For collection of strings, need to convert single-quoted string to double-quoted string,
                        // and also, per ABNF, a single quote within a string literal is "encoded" as two consecutive single quotes in either
                        // literal or percent - encoded representation.
                        // Sample: ['a''bc','''def','xyz'''] ==> ["a'bc","'def","xyz'"], which is legitimate Json format.
                        bracketLiteralText = NormalizeStringCollectionItems(bracketLiteralText);
                    }
                    else if (expectedTypeFullName.Equals("Edm.Guid", StringComparison.Ordinal))
                    {
                        // For collection of Guids, need to convert the Guid literals to single-quoted form, so that it is compatible
                        // with the Json reader used for deserialization.
                        // Sample: [D01663CF-EB21-4A0E-88E0-361C10ACE7FD, 492CF54A-84C9-490C-A7A4-B5010FAD8104]
                        //    ==>  ['D01663CF-EB21-4A0E-88E0-361C10ACE7FD', '492CF54A-84C9-490C-A7A4-B5010FAD8104']
                        bracketLiteralText = NormalizeGuidCollectionItems(bracketLiteralText);
                    }
                    else if (expectedTypeFullName.Equals("Edm.DateTimeOffset", StringComparison.Ordinal) ||
                             expectedTypeFullName.Equals("Edm.Date", StringComparison.Ordinal) ||
                             expectedTypeFullName.Equals("Edm.TimeOfDay", StringComparison.Ordinal) ||
                             expectedTypeFullName.Equals("Edm.Duration", StringComparison.Ordinal))
                    {
                        // For collection of Date/Time/Duration items, need to convert the Date/Time/Duration literals to single-quoted form, so that it is compatible
                        // with the Json reader used for deserialization.
                        // Sample: [1970-01-01T00:00:00Z, 1980-01-01T01:01:01+01:00]
                        //    ==>  ['1970-01-01T00:00:00Z', '1980-01-01T01:01:01+01:00']
                        bracketLiteralText = NormalizeDateTimeCollectionItems(bracketLiteralText);
                    }
                }

                object       collection             = ODataUriConversionUtils.ConvertFromCollectionValue(bracketLiteralText, model, expectedType);
                LiteralToken collectionLiteralToken = new LiteralToken(collection, originalLiteralText, expectedType);
                operand = this.bindMethod(collectionLiteralToken) as CollectionConstantNode;
            }
            else
            {
                operand = this.bindMethod(queryToken) as CollectionNode;
            }

            if (operand == null)
            {
                throw new ODataException(ODataErrorStrings.MetadataBinder_RightOperandNotCollectionValue);
            }

            return(operand);
        }
 /// <summary>
 /// Binds a literal token.
 /// </summary>
 /// <param name="literalToken">The literal token to bind.</param>
 /// <returns>The bound literal token.</returns>
 protected virtual QueryNode BindLiteral(LiteralToken literalToken)
 {
     return(LiteralBinder.BindLiteral(literalToken));
 }
 /// <summary>
 /// Visits a LiteralToken
 /// </summary>
 /// <param name="tokenIn">The LiteralToken to visit</param>
 /// <returns>A ConstantNode bound to this LambdaToken</returns>
 public virtual T Visit(LiteralToken tokenIn)
 {
     throw new NotImplementedException();
 }