/// <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 }); } }
/// <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)); }
private static bool IsStructuralOrNavigationPropertySelectionItem(SelectItem selectItem) { PathSelectItem pathSelectItem = selectItem as PathSelectItem; return(pathSelectItem != null && (pathSelectItem.SelectedPath.LastSegment is NavigationPropertySegment || pathSelectItem.SelectedPath.LastSegment is PropertySegment)); }
/// <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); }
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); }
/// <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); } }
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); }
/// <summary> /// Handle a PathSelectItem /// </summary> /// <param name="item">the item to Handle</param> public virtual void Handle(PathSelectItem item) { throw new NotImplementedException(); }
/// <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(); }