/// <summary> /// Parses the input for optionals. The resulting collection contains only literal and optinal segments which are expanded into the full tree later by the <see cref="M:Expand"/> method. /// </summary> /// <param name="input">The input path.</param> /// <returns>Returns the collection of literal and optional segments.</returns> protected virtual PathSegmentCollection ParseOptionals(string input) { Action <StringBuilder, Stack <PathSegmentCollection> > flush = (b, s) => { if (b.Length > 0) { s.Peek().Add(new LiteralPathSegment(b.ToString())); b.Length = 0; } }; var sb = new StringBuilder(); var stack = new Stack <PathSegmentCollection>(); stack.Push(new PathSegmentCollection()); for (int i = 0; i < input.Length; i++) { var ch = input[i]; if (ch == '(') { flush(sb, stack); var item = new OptionalPathSegment(); stack.Peek().Add(item); stack.Push(item.Segments); } else if (ch == ')') { if (stack.Count <= 1) { throw new FormatException("The number of closing braces is greater than the number of opening."); } flush(sb, stack); stack.Pop(); } else { sb.Append(ch); } } if (stack.Count > 1) { throw new FormatException(String.Format("Missing {0} closing brace(s).", stack.Count - 1)); } flush(sb, stack); return(stack.Peek()); }
/// <summary> /// Expands the specified segment collection which contains only optinal and literal segments. /// The optional segments are processed recursively and the literals are parsed for parameters. /// </summary> /// <param name="collection">The collection of segments to expand.</param> /// <param name="constraints">The constraints.</param> /// <param name="parameters">The parameters hash set to track the uniqueness.</param> /// <returns>Returns the expanded path segments tree.</returns> protected virtual PathSegmentCollection Expand(PathSegmentCollection collection, IDictionary <string, object> constraints, HashSet <string> parameters) { var result = new PathSegmentCollection(); foreach (var item in collection) { if (item is LiteralPathSegment) { var staticItem = (LiteralPathSegment)item; var list = Parse(staticItem.Text, constraints, parameters); result.AddRange(list); } else if (item is OptionalPathSegment) { var optItem = new OptionalPathSegment(); result.Add(optItem); var inner = Expand(item.Segments, constraints, parameters); optItem.Segments.AddRange(inner); } } return(result); }