Beispiel #1
0
        /// <summary>
        /// Get the string representation of a Pathselect item (that isn't an expandedNavPropSelectItem
        /// </summary>
        /// <param name="pathSelectItem">the pathselect item to translate</param>
        /// <returns>the string representation of this select pathselectitem</returns>
        private static IList <string> GetSelectStringFromPathSelectItem(PathSelectItem pathSelectItem)
        {
            IList <string> nextLevelSelectList = null;

            //Recursively call next level to get all the nested select items
            if (pathSelectItem.SelectAndExpand != null)
            {
                nextLevelSelectList = GetCurrentLevelSelectList(pathSelectItem.SelectAndExpand);
            }

            string selectListItem = String.Join("/", pathSelectItem.SelectedPath.WalkWith(PathSegmentToStringTranslator.Instance).ToArray());

            if (nextLevelSelectList != null && nextLevelSelectList.Any())
            {
                List <string> selectList = new List <string>();

                foreach (var listItem in nextLevelSelectList)
                {
                    selectList.Add(selectListItem + "/" + listItem);
                }

                return(selectList);
            }
            else
            {
                return(new List <string>()
                {
                    selectListItem
                });
            }
        }
Beispiel #2
0
        /// <summary>
        /// Bind the $expand <see cref="ExpandToken"/> and $select <see cref="SelectToken"/> at this level.
        /// </summary>
        /// <param name="expandToken">the expand token to visit.</param>
        /// <param name="selectToken">the select token to visit.</param>
        /// <returns>a SelectExpand clause based on <see cref="ExpandToken"/> and <see cref="SelectToken"/>.</returns>
        public SelectExpandClause Bind(ExpandToken expandToken, SelectToken selectToken)
        {
            List <SelectItem> selectExpandItems = new List <SelectItem>();

            // $expand=
            if (expandToken != null && expandToken.ExpandTerms.Any())
            {
                selectExpandItems.AddRange(expandToken.ExpandTerms.Select(this.GenerateExpandItem).Where(s => s != null));
            }

            // $select=
            bool isAllSelected = true;

            if (selectToken != null && selectToken.SelectTerms.Any())
            {
                // if there are any select items at this level then allSelected is false, otherwise it's true.
                isAllSelected = false;

                foreach (SelectTermToken selectTermToken in selectToken.SelectTerms)
                {
                    SelectItem     selectItem     = GenerateSelectItem(selectTermToken);
                    PathSelectItem selectPathItem = selectItem as PathSelectItem;
                    bool           duplicate      = false;

                    if (selectPathItem != null)
                    {
                        // It's not allowed to have multiple select clause with the same path.
                        // For example: $select=abc($top=2),abc($skip=2) is not allowed by design.
                        // Customer should combine them together, for example: $select=abc($top=2;$skip=2).
                        // The logic is different with ExpandTreeNormalizer. We should change the logic in ExpandTreeNormalizer
                        // in next breaking change version.
                        // For backward compatibility with previous versions of OData Library, we only validate
                        // if one of the select items has options.
                        foreach (PathSelectItem existingItem in selectExpandItems.OfType <PathSelectItem>())
                        {
                            if ((selectPathItem.HasOptions && overLaps(selectPathItem, existingItem)) || (existingItem.HasOptions && overLaps(existingItem, selectPathItem)))
                            {
                                throw new ODataException(ODataErrorStrings.SelectTreeNormalizer_MultipleSelecTermWithSamePathFound(ToPathString(selectTermToken.PathToProperty)));
                            }

                            // two items without options are identical -- for backward compat just ignore the new one
                            if (selectPathItem.SelectedPath.Equals(existingItem.SelectedPath))
                            {
                                duplicate = true;
                            }
                        }
                    }

                    if (!duplicate)
                    {
                        AddToSelectedItems(selectItem, selectExpandItems);
                    }
                }
            }

            // It's better to return "null" if both expand and select are null.
            // However, in order to be consistent, we returns empty "SelectExpandClause" with AllSelected = true.
            return(new SelectExpandClause(selectExpandItems, isAllSelected));
        }
Beispiel #3
0
        private static bool IsStructuralOrNavigationPropertySelectionItem(SelectItem selectItem)
        {
            PathSelectItem pathSelectItem = selectItem as PathSelectItem;

            return(pathSelectItem != null &&
                   (pathSelectItem.SelectedPath.LastSegment is NavigationPropertySegment ||
                    pathSelectItem.SelectedPath.LastSegment is PropertySegment));
        }
Beispiel #4
0
        /// <summary>
        /// Determines whether the first path is entirely contained in the second path.
        /// </summary>
        /// <param name="first">First path item</param>
        /// <param name="second">Second path item</param>
        /// <returns></returns>
        private static bool overLaps(PathSelectItem firstPath, PathSelectItem secondPath)
        {
            IEnumerator <ODataPathSegment> first  = firstPath.SelectedPath.GetEnumerator();
            IEnumerator <ODataPathSegment> second = secondPath.SelectedPath.GetEnumerator();

            bool completed;

            while ((completed = first.MoveNext()) && second.MoveNext() && first.Current.Identifier == second.Current.Identifier)
            {
            }

            return(!completed);
        }
Beispiel #5
0
        private static void AddToSelectedItems(SelectItem itemToAdd, List <SelectItem> selectItems)
        {
            if (itemToAdd == null)
            {
                return;
            }

            // ignore all property selection if there's a wildcard select item.
            WildcardSelectItem wildcardSelectItem = selectItems.OfType <WildcardSelectItem>().FirstOrDefault();

            if (wildcardSelectItem != null && IsStructuralOrNavigationPropertySelectionItem(itemToAdd))
            {
                wildcardSelectItem.AddSubsumed(itemToAdd);
                return;
            }

            // if the selected item is a nav prop, then see if its already there before we add it.
            PathSelectItem pathSelectItem = itemToAdd as PathSelectItem;

            if (pathSelectItem != null)
            {
                NavigationPropertySegment trailingNavPropSegment = pathSelectItem.SelectedPath.LastSegment as NavigationPropertySegment;
                if (trailingNavPropSegment != null)
                {
                    if (selectItems.OfType <PathSelectItem>().Any(i => i.SelectedPath.Equals(pathSelectItem.SelectedPath)))
                    {
                        return;
                    }
                }
            }

            // if the selected item is "*", filter the existing property selection.
            wildcardSelectItem = itemToAdd as WildcardSelectItem;
            if (wildcardSelectItem != null)
            {
                List <SelectItem> shouldFilter = selectItems.Where(s => IsStructuralSelectionItem(s)).ToList();
                wildcardSelectItem.AddSubsumed(shouldFilter);
                foreach (var filterItem in shouldFilter)
                {
                    selectItems.Remove(filterItem);
                }
            }

            selectItems.Add(itemToAdd);
        }
Beispiel #6
0
        /// <summary>
        /// Gets a list of strings representing current selected property name in current level.
        /// </summary>
        /// <param name="selectExpandClause">The select expand clause used.</param>
        /// <returns>String list generated from selected items</returns>
        internal static List <string> GetCurrentLevelSelectList(this SelectExpandClause selectExpandClause)
        {
            HashSet <string> levelSelectList  = new HashSet <string>();
            List <string>    masterSelectList = new List <string>();

            foreach (var selectItem in selectExpandClause.SelectedItems)
            {
                if (selectItem is WildcardSelectItem)
                {
                    levelSelectList.Add("*");
                    continue;
                }

                NamespaceQualifiedWildcardSelectItem namespaceQualifiedWildcard = selectItem as NamespaceQualifiedWildcardSelectItem;
                if (namespaceQualifiedWildcard != null)
                {
                    levelSelectList.Add(namespaceQualifiedWildcard.Namespace + ".*");
                    continue;
                }

                PathSelectItem pathSelectItem = selectItem as PathSelectItem;
                if (pathSelectItem != null)
                {
                    IList <string> pathSelectItems = GetSelectStringFromPathSelectItem(pathSelectItem);

                    for (int i = 0; i < pathSelectItems.Count; i++)
                    {
                        levelSelectList.Add(pathSelectItems[i]);
                    }
                }
            }

            // If a select item is a child of a complex property, and that complex property already has all properties selected,
            // then the child is redundant and should not be included in the contextUrl. i.e.; $select=address,address/city => address
            foreach (string item in levelSelectList)
            {
                if (!levelSelectList.Contains(GetPreviousSegments(item)))
                {
                    masterSelectList.Add(item);
                }
            }

            return(masterSelectList);
        }
        /// <summary>
        /// Get the string representation of a select item (that isn't an expandedNavPropSelectItem
        /// </summary>
        /// <param name="selectedItem">the select item to translate</param>
        /// <returns>the string representation of this select item, or null if the select item is an expandedNavPropSelectItem</returns>
        private static string GetSelectString(SelectItem selectedItem)
        {
            WildcardSelectItem wildcardSelect = selectedItem as WildcardSelectItem;
            NamespaceQualifiedWildcardSelectItem namespaceQualifiedWildcard = selectedItem as NamespaceQualifiedWildcardSelectItem;
            PathSelectItem pathSelectItem = selectedItem as PathSelectItem;

            if (wildcardSelect != null)
            {
                return("*");
            }
            else if (namespaceQualifiedWildcard != null)
            {
                return(namespaceQualifiedWildcard.Namespace + ".*");
            }
            else if (pathSelectItem != null)
            {
                return(String.Join("/", pathSelectItem.SelectedPath.WalkWith(PathSegmentToStringTranslator.Instance).ToArray()));
            }
            else
            {
                return(null);
            }
        }
Beispiel #8
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);
        }
Beispiel #9
0
 /// <summary>
 /// Handle a PathSelectItem
 /// </summary>
 /// <param name="item">the item to Handle</param>
 public virtual void Handle(PathSelectItem item)
 {
     throw new NotImplementedException();
 }
Beispiel #10
0
 /// <summary>
 /// Translate a PathSelectItem
 /// </summary>
 /// <param name="item">the item to Translate</param>
 /// <returns>Defined by the implementer</returns>
 public virtual T Translate(PathSelectItem item)
 {
     throw new NotImplementedException();
 }