/// <summary> /// Converts a string to a byte[] value. /// </summary> /// <param name="text">String text to convert.</param> /// <param name="targetValue">After invocation, converted value.</param> /// <returns>true if the value was converted; false otherwise.</returns> /// <remarks>Copy of WebConvert.TryKeyStringToByteArray.</remarks> private static bool TryUriStringToByteArray(string text, out byte[] targetValue) { Debug.Assert(text != null, "text != null"); if (!UriParserHelper.TryRemoveLiteralPrefix(ExpressionConstants.LiteralPrefixBinary, ref text)) { targetValue = null; return(false); } if (!UriParserHelper.TryRemoveQuotes(ref text)) { targetValue = null; return(false); } try { targetValue = Convert.FromBase64String(text); } catch (FormatException) { targetValue = null; return(false); } return(true); }
/// <summary> /// Parse the level option in the expand option text. /// </summary> /// <returns>The level option for expand in long type</returns> private long?ParseInnerLevel() { long?levelsOption = null; // advance to the equal sign this.lexer.NextToken(); string levelsText = UriParserHelper.ReadQueryOption(this.lexer); long level; if (string.Equals( ExpressionConstants.KeywordMax, levelsText, this.enableCaseInsensitiveBuiltinIdentifier ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) { levelsOption = long.MinValue; } else if (!long.TryParse(levelsText, NumberStyles.None, CultureInfo.InvariantCulture, out level) || level < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidLevelsOption(levelsText)); } else { levelsOption = level; } return(levelsOption); }
/// <summary> /// Parse the expand option in the select/expand option text. /// </summary> /// <param name="pathToken">The path segment token</param> /// <returns>The expand option for select/expand</returns> private ExpandToken ParseInnerExpand(PathSegmentToken pathToken) { // advance to the equal sign this.lexer.NextToken(); string expandText = UriParserHelper.ReadQueryOption(this.lexer); IEdmStructuredType targetStructuredType = null; if (this.resolver != null && this.parentStructuredType != null) { var parentProperty = this.resolver.ResolveProperty(parentStructuredType, pathToken.Identifier); // it is a property, need to find the type. // Like $expand=Friends($expand=Trips($expand=*)), when expandText becomes "Trips($expand=*)", // find navigation property Trips of Friends, then get Entity type of Trips. // or for select query like: $select=Address($expand=City) if (parentProperty != null) { targetStructuredType = parentProperty.Type.ToStructuredType(); } } SelectExpandParser innerExpandParser = new SelectExpandParser( resolver, expandText, targetStructuredType, this.maxRecursionDepth - 1, this.enableCaseInsensitiveBuiltinIdentifier, this.enableNoDollarQueryOptions); return(innerExpandParser.ParseExpand()); }
/// <summary> /// Tries to remove formatting specific to this parser's expected type. /// </summary> /// <param name="text">The text to remove formatting from.</param> /// <returns>Whether or not the expected formatting was found and successfully removed.</returns> internal virtual bool TryRemoveFormatting(ref string text) { if (this.prefix != null) { if (!UriParserHelper.TryRemovePrefix(this.prefix, ref text)) { return(false); } } bool shouldBeQuoted = this.prefix != null || ValueOfTypeCanContainQuotes(this.expectedType); if (shouldBeQuoted && !UriParserHelper.TryRemoveQuotes(ref text)) { return(false); } if (this.suffix != null) { // we need to try to remove the literal even if it isn't required. if (!TryRemoveLiteralSuffix(this.suffix, ref text) && this.suffixRequired) { return(false); } } return(true); }
/// <summary> /// Parse the select option in the select/expand option text. /// </summary> /// <param name="pathToken">The path segment token</param> /// <returns>The select option for select/expand</returns> private SelectToken ParseInnerSelect(PathSegmentToken pathToken) { // advance to the equal sign this.lexer.NextToken(); string selectText = UriParserHelper.ReadQueryOption(this.lexer); IEdmStructuredType targetStructuredType = null; if (this.resolver != null && this.parentStructuredType != null) { var parentProperty = this.resolver.ResolveProperty(parentStructuredType, pathToken.Identifier); // It is a property, need to find the type. // or for select query like: $select=Address($expand=City) if (parentProperty != null) { targetStructuredType = parentProperty.Type.ToStructuredType(); } } SelectExpandParser innerSelectParser = new SelectExpandParser( resolver, selectText, targetStructuredType, this.maxRecursionDepth - 1, this.enableCaseInsensitiveBuiltinIdentifier, this.enableNoDollarQueryOptions); return(innerSelectParser.ParseSelect()); }
/// <summary> /// Try to parse the given text to a Geometry object. /// </summary> /// <param name="text">Text to parse.</param> /// <param name="targetValue">Geometry to return.</param> /// <param name="parsingFailureReasonException">The detailed reason of parsing error.</param> /// <returns>True if succeeds, false if not.</returns> private static bool TryUriStringToGeometry(string text, out Geometry targetValue, out UriLiteralParsingException parsingFailureReasonException) { parsingFailureReasonException = null; if (!UriParserHelper.TryRemoveLiteralPrefix(ExpressionConstants.LiteralPrefixGeometry, ref text)) { targetValue = default(Geometry); return(false); } if (!UriParserHelper.TryRemoveQuotes(ref text)) { targetValue = default(Geometry); return(false); } try { targetValue = LiteralUtils.ParseGeometry(text); return(true); } catch (ParseErrorException e) { targetValue = default(Geometry); parsingFailureReasonException = new UriLiteralParsingException(e.Message); return(false); } }
/// <summary> /// Converts a string to a Duration value. /// </summary> /// <param name="text">String text to convert.</param> /// <param name="targetValue">After invocation, converted value.</param> /// <returns>true if the value was converted; false otherwise.</returns> /// <remarks>Copy of WebConvert.TryKeyStringToTime.</remarks> private static bool TryUriStringToDuration(string text, out TimeSpan targetValue) { if (!UriParserHelper.TryRemoveLiteralPrefix(ExpressionConstants.LiteralPrefixDuration, ref text)) { targetValue = default(TimeSpan); return(false); } if (!UriParserHelper.TryRemoveQuotes(ref text)) { targetValue = default(TimeSpan); return(false); } try { targetValue = EdmValueParser.ParseDuration(text); return(true); } catch (FormatException) { targetValue = default(TimeSpan); return(false); } }
/// <summary> /// Try to bind an identifier to a EnumNode /// </summary> /// <param name="identifier">the identifier to bind</param> /// <param name="typeReference">the enum typeReference</param> /// <param name="modelWhenNoTypeReference">the current model when no enum typeReference.</param> /// <param name="resolver">ODataUriResolver .</param> /// <param name="boundEnum">an enum node .</param> /// <returns>true if we bound an enum for this token.</returns> internal static bool TryBindIdentifier(string identifier, IEdmEnumTypeReference typeReference, IEdmModel modelWhenNoTypeReference, ODataUriResolver resolver, out QueryNode boundEnum) { boundEnum = null; string text = identifier; // parse the string, e.g., NS.Color'Green' // get type information, and also convert Green into an ODataEnumValue // find the first ', before that, it is namespace.type int indexOfSingleQuote = text.IndexOf('\''); if (indexOfSingleQuote < 0) { return(false); } string namespaceAndType = text.Substring(0, indexOfSingleQuote); Debug.Assert((typeReference == null) || (modelWhenNoTypeReference == null), "((typeReference == null) || (modelWhenNoTypeReference == null)"); // validate typeReference but allow type name not found in model for delayed throwing. if ((typeReference != null) && !string.Equals(namespaceAndType, typeReference.FullName(), StringComparison.Ordinal)) { return(false); } // get the type IEdmEnumType enumType = typeReference != null ? (IEdmEnumType)typeReference.Definition : UriEdmHelpers.FindEnumTypeFromModel(modelWhenNoTypeReference, namespaceAndType, resolver); if (enumType == null) { return(false); } // now, find out the value UriParserHelper.TryRemovePrefix(namespaceAndType, ref text); UriParserHelper.TryRemoveQuotes(ref text); // parse string or int value to edm enum value string enumValueString = text; ODataEnumValue enumValue; if (!TryParseEnum(enumType, enumValueString, out enumValue)) { return(false); } // create an enum node, enclosing an odata enum value IEdmEnumTypeReference enumTypeReference = typeReference ?? new EdmEnumTypeReference(enumType, false); boundEnum = new ConstantNode(enumValue, identifier, enumTypeReference); return(true); }
/// <summary> /// Parse the compute option in the expand option text. /// </summary> /// <returns>The compute option for expand</returns> private ComputeToken ParseInnerCompute() { this.lexer.NextToken(); string computeText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser computeParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(computeParser.ParseCompute(computeText)); }
/// <summary> /// Parse the apply option in the expand option text. /// </summary> /// <returns>The apply option for expand</returns> private IEnumerable <QueryToken> ParseInnerApply() { this.lexer.NextToken(); string applyText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser applyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(applyParser.ParseApply(applyText)); }
/// <summary> /// Parse the search option in the select/expand option text. /// </summary> /// <returns>The search option for select/expand</returns> private QueryToken ParseInnerSearch() { // advance to the equal sign this.lexer.NextToken(); string searchText = UriParserHelper.ReadQueryOption(this.lexer); SearchParser searchParser = new SearchParser(this.MaxSearchDepth); return(searchParser.ParseSearch(searchText)); }
/// <summary> /// Parse the filter option in the $count segment. /// </summary> /// <returns>The filter option for the $count segment.</returns> private QueryToken ParseInnerFilter() { // advance to the equal sign this.lexer.NextToken(); string filterText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(ODataUriParserSettings.DefaultFilterLimit, this.UriQueryExpressionParser.EnableCaseInsensitiveBuiltinIdentifier); return(filterParser.ParseFilter(filterText)); }
/// <summary> /// Parse the filter option in the select/expand option text. /// </summary> /// <returns>The filter option for select/expand</returns> private QueryToken ParseInnerFilter() { // advance to the equal sign this.lexer.NextToken(); string filterText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); return(filterParser.ParseFilter(filterText)); }
/// <summary> /// Parse the orderby option in the select/expand option text. /// </summary> /// <returns>The orderby option for select/expand</returns> private IEnumerable <OrderByToken> ParseInnerOrderBy() { // advance to the equal sign this.lexer.NextToken(); string orderByText = UriParserHelper.ReadQueryOption(this.lexer); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); return(orderbyParser.ParseOrderBy(orderByText)); }
/// <summary> /// Parse the search option in the $count segment. /// </summary> /// <returns>The search option for the $count segment.</returns> private QueryToken ParseInnerSearch() { // advance to the equal sign this.lexer.NextToken(); string searchText = UriParserHelper.ReadQueryOption(this.lexer); SearchParser searchParser = new SearchParser(ODataUriParserSettings.DefaultSearchLimit); return(searchParser.ParseSearch(searchText)); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the type of the current scope based on type segments.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver) { ExceptionUtils.CheckArgumentNotNull(resolver, "resolver"); ODataPathSegment nextSegment; if (UriParserHelper.IsAnnotation(tokenIn.Identifier)) { if (TryBindAsDeclaredTerm(tokenIn, model, resolver, out nextSegment)) { return(nextSegment); } string qualifiedTermName = tokenIn.Identifier.Remove(0, 1); int separator = qualifiedTermName.LastIndexOf(".", StringComparison.Ordinal); string namespaceName = qualifiedTermName.Substring(0, separator); string termName = qualifiedTermName.Substring(separator == 0 ? 0 : separator + 1); // Don't allow selecting odata control information if (String.Compare(namespaceName, ODataConstants.ODataPrefix, StringComparison.OrdinalIgnoreCase) == 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(tokenIn.Identifier)); } return(new AnnotationSegment(new EdmTerm(namespaceName, termName, EdmCoreModel.Instance.GetUntyped()))); } if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return(nextSegment); } // Operations must be container-qualified, and because the token type indicates it was not a .-seperated identifier, we should not try to look up operations. if (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return(nextSegment); } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return(null); } } if (edmType.IsOpen) { return(new DynamicPathSegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.FullTypeName(), tokenIn.Identifier)); }
internal IEdmTypeReference GetLiteralEdmTypeReference() { Debug.Assert(this.Kind != ExpressionTokenKind.CustomTypeLiteral || this.LiteralEdmType != null, "ExpressionTokenKind is marked as CustomTypeLiteral but not EdmType was set"); if (this.LiteralEdmType == null && this.Kind != ExpressionTokenKind.CustomTypeLiteral) { this.LiteralEdmType = UriParserHelper.GetLiteralEdmTypeReference(this.Kind); } return(this.LiteralEdmType); }
/// <summary> /// Remove the given literal prefix /// </summary> /// <param name="literalPrefix">The custom name of the literal prefix</param> /// <returns>'true' if the literal prefix is successfully found and removed; otherwise, 'false'.</returns> /// <exception cref="ArgumentNullException">Argument is null or empty</exception> public static bool RemoveCustomLiteralPrefix(string literalPrefix) { // Arguments validation ExceptionUtils.CheckArgumentStringNotNullOrEmpty(literalPrefix, "literalPrefix"); UriParserHelper.ValidatePrefixLiteral(literalPrefix); // Try to remove the custom uri literal prefix from cache lock (Locker) { return(CustomLiteralPrefixesOfEdmTypes.Remove(literalPrefix)); } }
/// <summary> /// Tries to remove formatting specific to this parser's expected type. /// </summary> /// <param name="text">The text to remove formatting from.</param> /// <returns> /// Whether or not the expected formatting was found and succesfully removed. /// </returns> internal override bool TryRemoveFormatting(ref string text) { if (!UriParserHelper.TryRemovePrefix(ExpressionConstants.LiteralPrefixBinary, ref text)) { return(false); } if (!UriParserHelper.TryRemoveQuotes(ref text)) { return(false); } return(true); }
/// <summary> /// Parse the skip option in the select/expand option text. /// </summary> /// <returns>The skip option for select/expand</returns> private long?ParseInnerSkip() { // advance to the equal sign this.lexer.NextToken(); string skipText = UriParserHelper.ReadQueryOption(this.lexer); // TryParse requires a non-nullable non-negative long. long skip; if (!long.TryParse(skipText, out skip) || skip < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText)); } return(skip); }
/// <summary> /// Parse the count option in the select/expand option text. /// </summary> /// <returns>The count option for select/expand</returns> private bool?ParseInnerCount() { // advance to the equal sign this.lexer.NextToken(); string countText = UriParserHelper.ReadQueryOption(this.lexer); switch (countText) { case ExpressionConstants.KeywordTrue: return(true); case ExpressionConstants.KeywordFalse: return(false); default: throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText)); } }
/// <summary> /// Tries to bind a given token as a declared annotation term. /// </summary> /// <param name="tokenIn">Token to bind.</param> /// <param name="model">The model to search for this term</param> /// <param name="resolver">Resolver for uri parser.</param> /// <param name="segment">Bound segment if the token was bound to a declared term successfully, or null.</param> /// <returns>True if the token was bound successfully, or false otherwise.</returns> private static bool TryBindAsDeclaredTerm(PathSegmentToken tokenIn, IEdmModel model, ODataUriResolver resolver, out ODataPathSegment segment) { if (!UriParserHelper.IsAnnotation(tokenIn.Identifier)) { segment = null; return(false); } IEdmTerm term = resolver.ResolveTerm(model, tokenIn.Identifier.Remove(0, 1)); if (term == null) { segment = null; return(false); } segment = new AnnotationSegment(term); return(true); }
/// <summary> /// Add a literal prefix for the given EdmType. /// </summary> /// <example>filter=MyProperty eq MyCustomLiteral'VALUE'. /// "MyCustomLiteral" is the literal prefix and the <paramref name="literalEdmTypeReference"/> is the type of the "VALUE".</example> /// <param name="literalPrefix">The custom name of the literal prefix</param> /// <param name="literalEdmTypeReference">The edm type of the custom literal</param> /// <exception cref="ArgumentNullException">Arguments are null or empty</exception> /// <exception cref="ArgumentException">The given literal prefix is not valid</exception> /// <exception cref="ODataException">The given literal prefix already exists</exception> public static void AddCustomLiteralPrefix(string literalPrefix, IEdmTypeReference literalEdmTypeReference) { // Arguments validation ExceptionUtils.CheckArgumentNotNull(literalEdmTypeReference, "literalEdmTypeReference"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(literalPrefix, "literalPrefix"); UriParserHelper.ValidatePrefixLiteral(literalPrefix); // Try to add the custom uri literal to cache lock (Locker) { // Check if literal does already exists if (CustomLiteralPrefixesOfEdmTypes.ContainsKey(literalPrefix)) { throw new ODataException(ODataErrorStrings.CustomUriTypePrefixLiterals_AddCustomUriTypePrefixLiteralAlreadyExists(literalPrefix)); } CustomLiteralPrefixesOfEdmTypes.Add(literalPrefix, literalEdmTypeReference); } }
/// <summary>Parses a token that starts with a digit.</summary> /// <returns>The kind of token recognized.</returns> private ExpressionTokenKind ParseFromDigit() { Debug.Assert(this.IsValidDigit || ('-' == this.ch), "this.IsValidDigit || ('-' == this.ch)"); ExpressionTokenKind result; int tokenPos = this.textPos; char startChar = this.ch.Value; this.NextChar(); if (startChar == '0' && (this.ch == 'x' || this.ch == 'X')) { result = ExpressionTokenKind.BinaryLiteral; do { this.NextChar(); }while (this.ch.HasValue && UriParserHelper.IsCharHexDigit(this.ch.Value)); } else { result = ExpressionTokenKind.IntegerLiteral; while (this.IsValidDigit) { this.NextChar(); } // DateTimeOffset, Date and Guids will have '-' in them if (this.ch == '-') { if (this.TryParseDate(tokenPos)) { return(ExpressionTokenKind.DateLiteral); } else if (this.TryParseDateTimeoffset(tokenPos)) { return(ExpressionTokenKind.DateTimeOffsetLiteral); } else if (this.TryParseGuid(tokenPos)) { return(ExpressionTokenKind.GuidLiteral); } } // TimeOfDay will have ":" in them if (this.ch == ':') { if (this.TryParseTimeOfDay(tokenPos)) { return(ExpressionTokenKind.TimeOfDayLiteral); } } // Guids will have alpha-numeric characters along with '-', so if a letter is encountered // try to see if this is Guid or not. if (this.ch.HasValue && Char.IsLetter(this.ch.Value)) { if (this.TryParseGuid(tokenPos)) { return(ExpressionTokenKind.GuidLiteral); } } if (this.ch == '.') { result = ExpressionTokenKind.DoubleLiteral; this.NextChar(); this.ValidateDigit(); do { this.NextChar(); }while (this.IsValidDigit); } if (this.ch == 'E' || this.ch == 'e') { result = ExpressionTokenKind.DoubleLiteral; this.NextChar(); if (this.ch == '+' || this.ch == '-') { this.NextChar(); } this.ValidateDigit(); do { this.NextChar(); }while (this.IsValidDigit); } if (this.ch == 'M' || this.ch == 'm') { result = ExpressionTokenKind.DecimalLiteral; this.NextChar(); } else if (this.ch == 'd' || this.ch == 'D') { result = ExpressionTokenKind.DoubleLiteral; this.NextChar(); } else if (this.ch == 'L' || this.ch == 'l') { result = ExpressionTokenKind.Int64Literal; this.NextChar(); } else if (this.ch == 'f' || this.ch == 'F') { result = ExpressionTokenKind.SingleLiteral; this.NextChar(); } else { string valueStr = this.Text.Substring(tokenPos, this.textPos - tokenPos); result = MakeBestGuessOnNoSuffixStr(valueStr, result); } } return(result); }
/// <summary> /// Process a <see cref="PathSegmentToken"/> following any type segments if necessary. /// </summary> /// <param name="tokenIn">the path token to process.</param> private List <ODataPathSegment> ProcessSelectTokenPath(PathSegmentToken tokenIn) { Debug.Assert(tokenIn != null, "tokenIn != null"); List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); IEdmStructuredType currentLevelType = this.edmType; // first, walk through all type segments in a row, converting them from tokens into segments. if (tokenIn.IsNamespaceOrContainerQualified() && !UriParserHelper.IsAnnotation(tokenIn.Identifier)) { PathSegmentToken firstNonTypeToken; pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(tokenIn, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelType, out firstNonTypeToken)); Debug.Assert(firstNonTypeToken != null, "Did not get last token."); tokenIn = firstNonTypeToken as NonSystemToken; if (tokenIn == null) { throw new ODataException(ODataErrorStrings.SelectExpandBinder_SystemTokenInSelect(firstNonTypeToken.Identifier)); } } // next, create a segment for the first non-type segment in the path. ODataPathSegment lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(tokenIn, this.Model, currentLevelType, this.configuration.Resolver, this.state); // next, create an ODataPath and add the segments to it. if (lastSegment != null) { pathSoFar.Add(lastSegment); // try create a complex type property path. while (true) { // no need to go on if the current property is not of complex type or collection of complex type, // unless the segment is a primitive type cast or a property on an open complex property. currentLevelType = lastSegment.EdmType as IEdmStructuredType; IEdmCollectionType collectionType = lastSegment.EdmType as IEdmCollectionType; IEdmPrimitiveType primitiveType = lastSegment.EdmType as IEdmPrimitiveType; DynamicPathSegment dynamicPath = lastSegment as DynamicPathSegment; if ((currentLevelType == null || currentLevelType.TypeKind != EdmTypeKind.Complex) && (collectionType == null || collectionType.ElementType.TypeKind() != EdmTypeKind.Complex) && (primitiveType == null || primitiveType.TypeKind != EdmTypeKind.Primitive) && (dynamicPath == null || tokenIn.NextToken == null)) { break; } NonSystemToken nextToken = tokenIn.NextToken as NonSystemToken; if (nextToken == null) { break; } if (UriParserHelper.IsAnnotation(nextToken.Identifier)) { lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.Model, currentLevelType, this.configuration.Resolver, null); } else if (primitiveType == null && dynamicPath == null) { // This means last segment a collection of complex type, // current segment can only be type cast and cannot be property name. if (currentLevelType == null) { currentLevelType = collectionType.ElementType.Definition as IEdmStructuredType; } // If there is no collection type in the path yet, will try to bind property for the next token // first try bind the segment as property. lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.Model, currentLevelType, this.configuration.Resolver, null); } else { // determine whether we are looking at a type cast or a dynamic path segment. EdmPrimitiveTypeKind nextTypeKind = EdmCoreModel.Instance.GetPrimitiveTypeKind(nextToken.Identifier); IEdmPrimitiveType castType = EdmCoreModel.Instance.GetPrimitiveType(nextTypeKind); if (castType != null) { lastSegment = new TypeSegment(castType, castType, null); } else if (dynamicPath != null) { lastSegment = new DynamicPathSegment(nextToken.Identifier); } else { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } } // then try bind the segment as type cast. if (lastSegment == null) { IEdmStructuredType typeFromNextToken = UriEdmHelpers.FindTypeFromModel(this.Model, nextToken.Identifier, this.configuration.Resolver) as IEdmStructuredType; if (typeFromNextToken.IsOrInheritsFrom(currentLevelType)) { lastSegment = new TypeSegment(typeFromNextToken, /*entitySet*/ null); } } // type cast failed too. if (lastSegment == null) { break; } // try move to and add next path segment. tokenIn = nextToken; pathSoFar.Add(lastSegment); } } // non-navigation cases do not allow further segments in $select. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } // Later, we can consider to create a "DynamicOperationSegment" to handle this. // But now, Let's throw exception. if (lastSegment == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_InvalidIdentifierInQueryOption(tokenIn.Identifier)); } // navigation property is not allowed to append sub path in the selection. NavigationPropertySegment navPropSegment = pathSoFar.LastOrDefault() as NavigationPropertySegment; if (navPropSegment != null && tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } return(pathSoFar); }
/// <summary> /// Tries to remove formatting specific to this parser's expected type. /// </summary> /// <param name="text">The text to remove formatting from.</param> /// <returns> /// Whether or not the expected formatting was found and succesfully removed. /// </returns> internal override bool TryRemoveFormatting(ref string text) { return(UriParserHelper.TryRemoveQuotes(ref text)); }
private bool TryUriStringToPrimitive(string text, IEdmTypeReference targetType, out object targetValue, out UriLiteralParsingException exception) { Debug.Assert(text != null, "text != null"); Debug.Assert(targetType != null, "targetType != null"); exception = null; try { if (targetType.IsNullable) { // COMPAT 38: Product does not support "null" literals in service operation arguments // check for the 'null' constant for nullable types if (text == ExpressionConstants.KeywordNull) { targetValue = null; return(true); } } IEdmPrimitiveTypeReference primitiveTargetType = targetType.AsPrimitiveOrNull(); if (primitiveTargetType == null) { targetValue = null; return(false); } EdmPrimitiveTypeKind targetTypeKind = primitiveTargetType.PrimitiveKind(); byte[] byteArrayValue; bool binaryResult = TryUriStringToByteArray(text, out byteArrayValue); if (targetTypeKind == EdmPrimitiveTypeKind.Binary) { targetValue = (object)byteArrayValue; return(binaryResult); } else if (binaryResult) { string keyValue = Encoding.UTF8.GetString(byteArrayValue, 0, byteArrayValue.Length); return(this.TryUriStringToPrimitive(keyValue, targetType, out targetValue)); } else if (targetTypeKind == EdmPrimitiveTypeKind.Guid) { Guid guidValue; bool result = UriUtils.TryUriStringToGuid(text, out guidValue); targetValue = guidValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Date) { Date dateValue; bool result = UriUtils.TryUriStringToDate(text, out dateValue); targetValue = dateValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.DateTimeOffset) { DateTimeOffset dateTimeOffsetValue; bool result = UriUtils.ConvertUriStringToDateTimeOffset(text, out dateTimeOffsetValue); targetValue = dateTimeOffsetValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Duration) { TimeSpan timespanValue; bool result = TryUriStringToDuration(text, out timespanValue); targetValue = timespanValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Geography) { Geography geographyValue; bool result = TryUriStringToGeography(text, out geographyValue, out exception); targetValue = geographyValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Geometry) { Geometry geometryValue; bool result = TryUriStringToGeometry(text, out geometryValue, out exception); targetValue = geometryValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.TimeOfDay) { TimeOfDay timeOfDayValue; bool result = UriUtils.TryUriStringToTimeOfDay(text, out timeOfDayValue); targetValue = timeOfDayValue; return(result); } bool quoted = targetTypeKind == EdmPrimitiveTypeKind.String; if (quoted != UriParserHelper.IsUriValueQuoted(text)) { targetValue = null; return(false); } if (quoted) { text = UriParserHelper.RemoveQuotes(text); } try { switch (targetTypeKind) { case EdmPrimitiveTypeKind.String: targetValue = text; break; case EdmPrimitiveTypeKind.Boolean: targetValue = XmlConvert.ToBoolean(text); break; case EdmPrimitiveTypeKind.Byte: targetValue = XmlConvert.ToByte(text); break; case EdmPrimitiveTypeKind.SByte: targetValue = XmlConvert.ToSByte(text); break; case EdmPrimitiveTypeKind.Int16: targetValue = XmlConvert.ToInt16(text); break; case EdmPrimitiveTypeKind.Int32: targetValue = XmlConvert.ToInt32(text); break; case EdmPrimitiveTypeKind.Int64: UriParserHelper.TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixInt64, ref text); targetValue = XmlConvert.ToInt64(text); break; case EdmPrimitiveTypeKind.Single: UriParserHelper.TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixSingle, ref text); targetValue = XmlConvert.ToSingle(text); break; case EdmPrimitiveTypeKind.Double: UriParserHelper.TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDouble, ref text); targetValue = XmlConvert.ToDouble(text); break; case EdmPrimitiveTypeKind.Decimal: UriParserHelper.TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDecimal, ref text); try { targetValue = XmlConvert.ToDecimal(text); } catch (FormatException) { // we need to support exponential format for decimals since we used to support them in V1 decimal result; if (Decimal.TryParse(text, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out result)) { targetValue = result; } else { targetValue = default(Decimal); return(false); } } break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_TryUriStringToPrimitive)); } return(true); } catch (FormatException) { targetValue = null; return(false); } catch (OverflowException) { targetValue = null; return(false); } } catch (Exception primitiveParserException) { exception = new UriLiteralParsingException( string.Format(CultureInfo.InvariantCulture, ODataErrorStrings.UriPrimitiveTypeParsers_FailedToParseTextToPrimitiveValue(text, targetType), primitiveParserException)); targetValue = null; return(false); } }
/// <summary> /// Function to normalize quoted string, ensuring single quotes are escaped properly. /// If the string is double-quoted, no op since single quote doesn't need to be escaped. /// </summary> /// <param name="str">The quoted string item to be normalized.</param> /// <returns>The double-quoted string with single quotes properly escaped.</returns> private static string NormalizeStringItem(string str) { // Validate the string item is quoted properly. if (!((str[0] == '\'' && str[str.Length - 1] == '\'') || (str[0] == '"' && str[str.Length - 1] == '"'))) { throw new ODataException(ODataErrorStrings.StringItemShouldBeQuoted(str)); } // Skip conversion if the items are already in double-quote format (for backward compatibility). // Note that per ABNF, query option strings should use single quotes. string convertedString = str; if (str[0] == '\'') { convertedString = String.Format(CultureInfo.InvariantCulture, "\"{0}\"", UriParserHelper.RemoveQuotes(str)); } return(convertedString); }
private void ProcessTokenAsPath(NonSystemToken tokenIn) { Debug.Assert(tokenIn != null, "tokenIn != null"); List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); IEdmStructuredType currentLevelType = this.edmType; // first, walk through all type segments in a row, converting them from tokens into segments. if (tokenIn.IsNamespaceOrContainerQualified() && !UriParserHelper.IsAnnotation(tokenIn.Identifier)) { PathSegmentToken firstNonTypeToken; pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(tokenIn, this.model, this.maxDepth, this.resolver, ref currentLevelType, out firstNonTypeToken)); Debug.Assert(firstNonTypeToken != null, "Did not get last token."); tokenIn = firstNonTypeToken as NonSystemToken; if (tokenIn == null) { throw new ODataException(ODataErrorStrings.SelectPropertyVisitor_SystemTokenInSelect(firstNonTypeToken.Identifier)); } } // next, create a segment for the first non-type segment in the path. ODataPathSegment lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(tokenIn, this.model, currentLevelType, resolver, this.state); // next, create an ODataPath and add the segments to it. if (lastSegment != null) { pathSoFar.Add(lastSegment); // try create a complex type property path. while (true) { // no need to go on if the current property is not of complex type or collection of complex type, // unless the segment is a primitive type cast or a property on an open complex property. currentLevelType = lastSegment.EdmType as IEdmStructuredType; IEdmCollectionType collectionType = lastSegment.EdmType as IEdmCollectionType; IEdmPrimitiveType primitiveType = lastSegment.EdmType as IEdmPrimitiveType; DynamicPathSegment dynamicPath = lastSegment as DynamicPathSegment; if ((currentLevelType == null || currentLevelType.TypeKind != EdmTypeKind.Complex) && (collectionType == null || collectionType.ElementType.TypeKind() != EdmTypeKind.Complex) && (primitiveType == null || primitiveType.TypeKind != EdmTypeKind.Primitive) && (dynamicPath == null || tokenIn.NextToken == null)) { break; } NonSystemToken nextToken = tokenIn.NextToken as NonSystemToken; if (nextToken == null) { break; } if (UriParserHelper.IsAnnotation(nextToken.Identifier)) { lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.model, currentLevelType, resolver, null); } else if (primitiveType == null && dynamicPath == null) { // This means last segment a collection of complex type, // current segment can only be type cast and cannot be property name. if (currentLevelType == null) { currentLevelType = collectionType.ElementType.Definition as IEdmStructuredType; } // If there is no collection type in the path yet, will try to bind property for the next token // first try bind the segment as property. lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.model, currentLevelType, resolver, null); } else { // determine whether we are looking at a type cast or a dynamic path segment. EdmPrimitiveTypeKind nextTypeKind = EdmCoreModel.Instance.GetPrimitiveTypeKind(nextToken.Identifier); IEdmPrimitiveType castType = EdmCoreModel.Instance.GetPrimitiveType(nextTypeKind); if (castType != null) { lastSegment = new TypeSegment(castType, castType, null); } else if (dynamicPath != null) { lastSegment = new DynamicPathSegment(nextToken.Identifier); } else { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } } // then try bind the segment as type cast. if (lastSegment == null) { IEdmStructuredType typeFromNextToken = UriEdmHelpers.FindTypeFromModel(this.model, nextToken.Identifier, this.resolver) as IEdmStructuredType; if (typeFromNextToken.IsOrInheritsFrom(currentLevelType)) { lastSegment = new TypeSegment(typeFromNextToken, /*entitySet*/ null); } } // type cast failed too. if (lastSegment == null) { break; } // try move to and add next path segment. tokenIn = nextToken; pathSoFar.Add(lastSegment); } } ODataSelectPath selectedPath = new ODataSelectPath(pathSoFar); var selectionItem = new PathSelectItem(selectedPath); // non-navigation cases do not allow further segments in $select. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } // if the selected item is a nav prop, then see if its already there before we add it. NavigationPropertySegment trailingNavPropSegment = selectionItem.SelectedPath.LastSegment as NavigationPropertySegment; if (trailingNavPropSegment != null) { if (this.expandClauseToDecorate.SelectedItems.Any(x => x is PathSelectItem && ((PathSelectItem)x).SelectedPath.Equals(selectedPath))) { return; } } this.expandClauseToDecorate.AddToSelectedItems(selectionItem); }