/// <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); } }
/// <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); }
/// <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); }
/// <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); }
/// <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(); }