/// <summary> /// Parses the segments of a path in the select clause. /// </summary> /// <param name="segments">The segments of the select path.</param> /// <param name="index">The index of the segment to parse.</param> private void ParsePathSegment(string[] segments, int index) { Debug.Assert(segments != null, "segments != null"); Debug.Assert(index >= 0 && index < segments.Length, "index >= 0 && index < segments.Length"); // NOTE: Each path is the name of a property or a series of property names // separated by slash ('/'). The special star ('*') character is only supported at the end of a path. string currentSegment = segments[index].Trim(); if (this.selectedProperties == null) { this.selectedProperties = CreateSelectedPropertiesHashSet(); } bool isStar = string.CompareOrdinal(StarSegment, currentSegment) == 0; int idxLP = currentSegment.IndexOf('('); if (idxLP != -1 && IsValidExpandToken(currentSegment)) { string token = currentSegment.Substring(0, idxLP); SelectedPropertiesNode childNode = this.EnsureChildAnnotation(token, /* isExpandedNavigationProperty */ true); childNode.edmModel = this.edmModel; if (idxLP < currentSegment.Length - 2) { string clause = currentSegment.Substring(idxLP + 1, currentSegment.Length - idxLP - 2).Trim(); if (!String.IsNullOrEmpty(clause)) { // Setup the edm model and structured type for the child node before start parsing the select clause. IEdmNavigationProperty navProp = this.structuredType?.DeclaredNavigationProperties() ?.SingleOrDefault(p => p.Name.Equals(token, StringComparison.Ordinal)); if (navProp?.Type != null) { // navigation property could be structural type or collection of structural type. childNode.structuredType = navProp.Type.Definition.AsElementType() as IEdmStructuredType; } childNode.Parse(clause); } } else { childNode.selectionType = SelectionType.EntireSubtree; } } else { bool isLastSegment = index == segments.Length - 1; if (!isLastSegment) { if (isStar) { throw new ODataException(ODataErrorStrings.SelectedPropertiesNode_StarSegmentNotLastSegment); } SelectedPropertiesNode childNode = this.EnsureChildAnnotation(currentSegment, false); childNode.ParsePathSegment(segments, index + 1); } else { this.selectedProperties.Add(currentSegment); } } this.hasWildcard |= isStar; }
/// <summary> /// Recursively combines the left and right nodes. Used when there are type segments present in the select paths which /// causes there to be multiple children for the same property/navigation. /// </summary> /// <param name="left">The left node.</param> /// <param name="right">The right node.</param> /// <returns>The combined node.</returns> internal static SelectedPropertiesNode CombineNodes(SelectedPropertiesNode left, SelectedPropertiesNode right) { Debug.Assert(left != null, "left != null"); Debug.Assert(right != null, "right != null"); // if either one includes the entire subtree, then so does the result if (left.selectionType == SelectionType.EntireSubtree || right.selectionType == SelectionType.EntireSubtree) { return(new SelectedPropertiesNode(SelectionType.EntireSubtree)); } // if the left hand side is empty, then use the right hand side if (left.selectionType == SelectionType.Empty) { // even if this is empty too, this all works return(right); } // likewise, if the right hand side is empty, use the left if (right.selectionType == SelectionType.Empty) { return(left); } Debug.Assert(left.selectionType == SelectionType.PartialSubtree, "left.selectionType == SelectionType.PartialSubtree"); Debug.Assert(right.selectionType == SelectionType.PartialSubtree, "right.selectionType == SelectionType.PartialSubtree"); var combined = new SelectedPropertiesNode(SelectionType.PartialSubtree) { hasWildcard = left.hasWildcard | right.hasWildcard }; // copy over selected properties, combining as needed if (left.selectedProperties != null && right.selectedProperties != null) { combined.selectedProperties = CreateSelectedPropertiesHashSet(left.selectedProperties.AsEnumerable().Concat(right.selectedProperties)); } else if (left.selectedProperties != null) { combined.selectedProperties = CreateSelectedPropertiesHashSet(left.selectedProperties); } else if (right.selectedProperties != null) { combined.selectedProperties = CreateSelectedPropertiesHashSet(right.selectedProperties); } // copy over children, combining as needed if (left.children != null && right.children != null) { combined.children = new Dictionary <string, SelectedPropertiesNode>(left.children); foreach (var child in right.children) { SelectedPropertiesNode fromLeft; if (combined.children.TryGetValue(child.Key, out fromLeft)) { combined.children[child.Key] = CombineNodes(fromLeft, child.Value); } else { combined.children[child.Key] = child.Value; } } } else if (left.children != null) { combined.children = new Dictionary <string, SelectedPropertiesNode>(left.children); } else if (right.children != null) { combined.children = new Dictionary <string, SelectedPropertiesNode>(right.children); } return(combined); }