Ejemplo n.º 1
0
        /// <summary>
        /// Get the path string for a path segment token.
        /// </summary>
        /// <param name="head">The head of the path</param>
        /// <returns>The path string.</returns>
        internal static string ToPathString(PathSegmentToken head)
        {
            StringBuilder    sb   = new StringBuilder();
            PathSegmentToken curr = head;

            while (curr != null)
            {
                sb.Append(curr.Identifier);

                NonSystemToken nonSystem = curr as NonSystemToken;
                if (nonSystem != null && nonSystem.NamedValues != null)
                {
                    sb.Append("(");
                    bool first = true;
                    foreach (var item in nonSystem.NamedValues)
                    {
                        if (first)
                        {
                            first = false;
                        }
                        else
                        {
                            sb.Append(",");
                        }

                        sb.Append(item.Name).Append("=").Append(item.Value.Value);
                    }

                    sb.Append(")");
                }

                curr = curr.NextToken;
                if (curr != null)
                {
                    sb.Append("/");
                }
            }

            return(sb.ToString());
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Building off of a PathSegmentToken whose value is star, only nested level options is allowed.
        /// </summary>
        /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param>
        /// <returns>An expand term token based on the path token, and all available expand options.</returns>
        private List <ExpandTermToken> BuildStarExpandTermToken(PathSegmentToken pathToken)
        {
            List <ExpandTermToken> expandTermTokenList = new List <ExpandTermToken>();
            long?levelsOption = null;
            bool isRefExpand  = (pathToken.Identifier == UriQueryConstants.RefSegment);

            // Based on the specification,
            //   For star in expand, this will be supported,
            //   $expand=*
            //   $expand=EntitySet($expand=* )
            //   $expand=*/$ref
            //   $expand=*,EntitySet
            //   $expand=EntitySet, *
            //   $expand=*/$ref,EntitySet
            //   Parenthesized set of expand options for star expand option supported are $level per specification.
            //   And this will throw exception,
            //   $expand= * /$count
            //   Parenthesized set of expand options for star expand option which will also cause exception are $filter, $select, $orderby, $skip, $top, $count, $search, and $expand per specification.
            // And level is not supported with "*/$ref".

            // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future.
            if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen)
            {
                // advance past the '('
                this.lexer.NextToken();

                // Check for (), which is not allowed.
                if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen)
                {
                    throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier));
                }

                // Only level option is supported by expand.
                while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen)
                {
                    string text = this.enableCaseInsensitiveBuiltinIdentifier
                        ? this.lexer.CurrentToken.Text.ToLowerInvariant()
                        : this.lexer.CurrentToken.Text;
                    switch (text)
                    {
                    case ExpressionConstants.QueryOptionLevels:
                    {
                        if (!isRefExpand)
                        {
                            levelsOption = ParseInnerLevel();
                        }
                        else
                        {
                            // no option is allowed when expand with star per specification
                            throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStarRef(this.lexer.ExpressionText));
                        }

                        break;
                    }

                    default:
                    {
                        throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStar(this.lexer.ExpressionText));
                    }
                    }
                }

                // Move past the ')'
                this.lexer.NextToken();
            }

            // Either there was no '(' at all or we just read past the ')' so we should be at the end
            if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText));
            }

            // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future.
            var entityType = this.parentStructuredType as IEdmEntityType;

            if (entityType == null)
            {
                throw new ODataException(ODataErrorStrings.UriExpandParser_ParentEntityIsNull(this.lexer.ExpressionText));
            }

            foreach (var navigationProperty in entityType.NavigationProperties())
            {
                var tmpPathToken = default(PathSegmentToken);

                // create path token for each navigation properties.
                if (pathToken.Identifier.Equals(UriQueryConstants.RefSegment))
                {
                    tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken.NextToken);
                    tmpPathToken = new NonSystemToken(UriQueryConstants.RefSegment, null, tmpPathToken);
                }
                else
                {
                    tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken);
                }

                ExpandTermToken currentToken = new ExpandTermToken(tmpPathToken, null, null,
                                                                   null, null, null, levelsOption, null, null, null, null, null);
                expandTermTokenList.Add(currentToken);
            }

            return(expandTermTokenList);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Tries to bind a given token as an Operation.
        /// </summary>
        /// <param name="pathToken">Token to bind.</param>
        /// <param name="model">The model.</param>
        /// <param name="entityType">the current entity type to use as the binding type when looking for operations.</param>
        /// <param name="segment">Bound segment if the token was bound to an operation successfully, or null.</param>
        /// <returns>True if the token was bound successfully, or false otherwise.</returns>
        internal static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel model, IEdmStructuredType entityType, out ODataPathSegment segment)
        {
            Debug.Assert(pathToken != null, "pathToken != null");
            Debug.Assert(entityType != null, "bindingType != null");

            IEnumerable <IEdmOperation> possibleFunctions = Enumerable.Empty <IEdmOperation>();
            IList <string> parameterNames = new List <string>();

            // Catch all catchable exceptions as FindDeclaredBoundOperations is implemented by anyone.
            // If an exception occurs it will be suppressed and the possible functions will be empty and return false.
            try
            {
                int wildCardPos = pathToken.Identifier.IndexOf("*", StringComparison.Ordinal);
                if (wildCardPos > -1)
                {
                    string namespaceName = pathToken.Identifier.Substring(0, wildCardPos - 1);
                    possibleFunctions = model.FindBoundOperations(entityType).Where(o => o.Namespace == namespaceName);
                }
                else
                {
                    NonSystemToken nonSystemToken = pathToken as NonSystemToken;
                    if (nonSystemToken != null && nonSystemToken.NamedValues != null)
                    {
                        parameterNames = nonSystemToken.NamedValues.Select(s => s.Name).ToList();
                    }

                    if (parameterNames.Count > 0)
                    {
                        // Always force to use fully qualified name when select operation
                        possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).FilterOperationsByParameterNames(parameterNames, false);
                    }
                    else
                    {
                        possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier);
                    }
                }
            }
            catch (Exception exc)
            {
                if (!ExceptionUtils.IsCatchableExceptionType(exc))
                {
                    throw;
                }
            }

            // Only filter if there is more than one and its needed.
            if (possibleFunctions.Count() > 1)
            {
                possibleFunctions = possibleFunctions.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(entityType);
            }

            // If more than one overload matches, try to select based on optional parameters
            if (possibleFunctions.Count() > 1 && parameterNames.Count > 0)
            {
                possibleFunctions = possibleFunctions.FilterOverloadsBasedOnParameterCount(parameterNames.Count);
            }

            if (!possibleFunctions.HasAny())
            {
                segment = null;
                return(false);
            }

            possibleFunctions.EnsureOperationsBoundWithBindingParameter();

            segment = new OperationSegment(possibleFunctions, null /*entitySet*/);
            return(true);
        }
Ejemplo n.º 4
0
        /// <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);
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
 /// <summary>
 /// Visit an NonSystemToken
 /// </summary>
 /// <param name="tokenIn">The System token to visit</param>
 /// <returns>A user defined class</returns>
 public virtual T Visit(NonSystemToken tokenIn)
 {
     throw new NotImplementedException();
 }