protected override void RenderDictionary(StringBuilder builder, VariableReference variable, IDictionary variableValue, bool first) { bool firstElement = true; foreach (DictionaryEntry entry in variableValue) { if (variable.Composite) { builder.Append('.'); AppendText(builder, variable, entry.Key.ToString(), true); builder.Append('='); AppendText(builder, variable, entry.Value.ToString(), true); } else { RenderElement(builder, variable, entry.Key, first, firstElement); RenderElement(builder, variable, entry.Value, first, false); } firstElement = false; } }
private void RenderElement(StringBuilder builder, VariableReference variable, object variableValue, bool firstVariable, bool firstElement) { if (builder == null) { throw new ArgumentNullException("builder"); } if (variableValue == null) { throw new ArgumentNullException("variableValue"); } if (firstElement || variable.Composite) { builder.Append("/"); } else if (!firstElement) { builder.Append(','); } AppendText(builder, variable, variableValue.ToString(), true); }
private void RenderElement(StringBuilder builder, VariableReference variable, object variableValue, bool firstVariable, bool firstElement) { if (builder == null) { throw new ArgumentNullException("builder"); } if (variableValue == null) { throw new ArgumentNullException("variableValue"); } if (firstElement || variable.Composite) { builder.Append(Type == UriTemplatePartType.Query && firstVariable && firstElement ? '?' : '&').Append(variable.Name).Append('='); } else if (!firstElement) { builder.Append(','); } AppendText(builder, variable, variableValue.ToString(), true); }
protected override void RenderElement(StringBuilder builder, VariableReference variable, object variableValue, bool first) { if (builder == null) { throw new ArgumentNullException("builder"); } if (variableValue == null) { throw new ArgumentNullException("variableValue"); } if (first) { builder.Append('#'); } if (!first) { builder.Append(','); } AppendText(builder, variable, variableValue.ToString(), false); }
/// <summary> /// Render a single variable, where the variable value is a <see cref="string"/> or other single-valued /// element. /// </summary> /// <param name="builder">The <see cref="StringBuilder"/> to render to.</param> /// <param name="variable">The variable being rendered.</param> /// <param name="variableValue">The value of the variable being rendered.</param> /// <param name="first"> /// <see langword="true"/> if this is the first variable being rendered from this expression; otherwise, /// <see langword="false"/>. Variables which do not have an associated parameter, or whose parameter value /// is <see langword="null"/>, are treated as though they were completely omitted for the purpose of /// determining the first variable. /// </param> /// <exception cref="ArgumentNullException"> /// <para>If <paramref name="builder"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variable"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variableValue"/> is <see langword="null"/>.</para> /// </exception> protected abstract void RenderElement(StringBuilder builder, VariableReference variable, object variableValue, bool first);
/// <summary> /// Render a single variable, where the variable value is a collection (<see cref="IEnumerable"/>). /// </summary> /// <param name="builder">The <see cref="StringBuilder"/> to render to.</param> /// <param name="variable">The variable being rendered.</param> /// <param name="variableValue">The value of the variable being rendered.</param> /// <param name="first"> /// <see langword="true"/> if this is the first variable being rendered from this expression; otherwise, /// <see langword="false"/>. Variables which do not have an associated parameter, or whose parameter value /// is <see langword="null"/>, are treated as though they were completely omitted for the purpose of /// determining the first variable. /// </param> /// <exception cref="ArgumentNullException"> /// <para>If <paramref name="builder"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variable"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variableValue"/> is <see langword="null"/>.</para> /// </exception> protected abstract void RenderEnumerable(StringBuilder builder, VariableReference variable, IEnumerable variableValue, bool first);
/// <summary> /// Render a single variable, where the variable value is an associative map (<see cref="IDictionary"/>). /// </summary> /// <param name="builder">The <see cref="StringBuilder"/> to render to.</param> /// <param name="variable">The variable being rendered.</param> /// <param name="variableValue">The value of the variable being rendered.</param> /// <param name="first"> /// <see langword="true"/> if this is the first variable being rendered from this expression; otherwise, /// <see langword="false"/>. Variables which do not have an associated parameter, or whose parameter value /// is <see langword="null"/>, are treated as though they were completely omitted for the purpose of /// determining the first variable. /// </param> /// <exception cref="ArgumentNullException"> /// <para>If <paramref name="builder"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variable"/> is <see langword="null"/>.</para> /// <para>-or-</para> /// <para>If <paramref name="variableValue"/> is <see langword="null"/>.</para> /// </exception> protected abstract void RenderDictionary(StringBuilder builder, VariableReference variable, IDictionary variableValue, bool first);
private static string BuildVariablePattern(VariableReference variable, bool allowReservedSet, string groupName, ICollection <string> requiredVariables, ICollection <string> arrayVariables, ICollection <string> mapVariables) { string characterPattern; if (allowReservedSet) { characterPattern = "(?:" + UnreservedCharacterPattern + "|" + ReservedCharacterPattern + ")"; } else { characterPattern = "(?:" + UnreservedCharacterPattern + ")"; } string valueStartPattern; if (!string.IsNullOrEmpty(groupName)) { valueStartPattern = "(?<" + groupName + ">"; } else { valueStartPattern = "(?:"; } string valueEndPattern = ")"; string keyStartPattern; if (!string.IsNullOrEmpty(groupName)) { keyStartPattern = "(?<" + groupName + "key>"; } else { keyStartPattern = "(?:"; } string keyEndPattern = ")"; string mapValueStartPattern; if (!string.IsNullOrEmpty(groupName)) { mapValueStartPattern = "(?<" + groupName + "value>"; } else { mapValueStartPattern = "(?:"; } string mapValueEndPattern = ")"; string countPattern; if (allowReservedSet) { countPattern = "*?"; } else { countPattern = "*"; } StringBuilder variablePattern = new StringBuilder(); if (variable.Prefix != null) { // by this point we know to match the variable as a simple string variablePattern.Append(valueStartPattern); variablePattern.Append(characterPattern); variablePattern.Append("{0,").Append(variable.Prefix).Append("}"); variablePattern.Append(valueEndPattern); return(variablePattern.ToString()); } bool treatAsArray = arrayVariables.Contains(variable.Name); bool treatAsMap = mapVariables.Contains(variable.Name); bool considerString = !variable.Composite && !treatAsArray && !treatAsMap; bool considerArray = treatAsArray || !treatAsMap; bool considerMap = treatAsMap || !treatAsArray; variablePattern.Append("(?:"); if (considerString) { // could be a simple string variablePattern.Append(valueStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(valueEndPattern); } if (considerArray) { if (considerString) { variablePattern.Append('|'); } // could be an associative array variablePattern.Append(valueStartPattern).Append(characterPattern).Append(countPattern).Append(valueEndPattern); variablePattern.Append("(?:,"); variablePattern.Append(valueStartPattern).Append(characterPattern).Append(countPattern).Append(valueEndPattern); variablePattern.Append(")*?"); } if (considerMap) { if (considerString || considerArray) { variablePattern.Append('|'); } // could be an associative map char separator = variable.Composite ? '=' : ','; variablePattern.Append(valueStartPattern); variablePattern.Append(keyStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(keyEndPattern); variablePattern.Append(separator).Append(mapValueStartPattern).Append(characterPattern).Append(countPattern).Append(mapValueEndPattern); variablePattern.Append(valueEndPattern); variablePattern.Append("(?:,"); variablePattern.Append(valueStartPattern); variablePattern.Append(keyStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(keyEndPattern); variablePattern.Append(separator).Append(mapValueStartPattern).Append(characterPattern).Append(countPattern).Append(mapValueEndPattern); variablePattern.Append(valueEndPattern); variablePattern.Append(")*?"); } variablePattern.Append(")"); return(variablePattern.ToString()); }
protected override KeyValuePair <VariableReference, object>[] MatchImpl(string text, ICollection <string> requiredVariables, ICollection <string> arrayVariables, ICollection <string> mapVariables) { BitArray requiredPatterns = new BitArray(Variables.Count); List <string> variablePatterns = new List <string>(); for (int i = 0; i < Variables.Count; i++) { VariableReference variable = Variables[i]; if (requiredVariables.Contains(variable.Name)) { requiredPatterns.Set(i, true); } bool allowReservedSet = Type == UriTemplatePartType.ReservedStringExpansion; variablePatterns.Add(BuildVariablePattern(Variables[i], allowReservedSet, "var" + i, requiredVariables, arrayVariables, mapVariables)); } StringBuilder matchPattern = new StringBuilder(); matchPattern.Append("^"); AppendOneOrMoreToEnd(matchPattern, requiredPatterns, variablePatterns, 0); matchPattern.Append("$"); Match match = Regex.Match(text, matchPattern.ToString()); List <KeyValuePair <VariableReference, object> > results = new List <KeyValuePair <VariableReference, object> >(); for (int i = 0; i < Variables.Count; i++) { Group group = match.Groups["var" + i]; if (!group.Success || group.Captures.Count == 0) { continue; } if (Variables[i].Prefix != null) { if (group.Success && group.Captures.Count == 1) { results.Add(new KeyValuePair <VariableReference, object>(Variables[i], DecodeCharacters(group.Captures[0].Value))); } continue; } bool treatAsArray = arrayVariables.Contains(Variables[i].Name); bool treatAsMap = mapVariables.Contains(Variables[i].Name); bool considerString = !Variables[i].Composite && !treatAsArray && !treatAsMap; bool considerArray = treatAsArray || !treatAsMap; bool considerMap = treatAsMap || !treatAsArray; // first check for a map Group mapKeys = match.Groups["var" + i + "key"]; if (mapKeys.Success && mapKeys.Captures.Count > 0) { Debug.Assert(considerMap, "considerMap"); Group mapValues = match.Groups["var" + i + "value"]; Dictionary <string, string> map = new Dictionary <string, string>(); for (int j = 0; j < mapKeys.Captures.Count; j++) { map.Add(DecodeCharacters(mapKeys.Captures[j].Value), DecodeCharacters(mapValues.Captures[j].Value)); } results.Add(new KeyValuePair <VariableReference, object>(Variables[i], map)); continue; } // next try an array if (!considerString || group.Captures.Count > 1) { Debug.Assert(considerArray, "considerArray"); List <string> list = new List <string>(group.Captures.Count); foreach (Capture capture in group.Captures) { list.Add(DecodeCharacters(capture.Value)); } results.Add(new KeyValuePair <VariableReference, object>(Variables[i], list)); continue; } Debug.Assert(considerString, "considerString"); results.Add(new KeyValuePair <VariableReference, object>(Variables[i], DecodeCharacters(group.Captures[0].Value))); } return(results.ToArray()); }
protected override void RenderElement(StringBuilder builder, VariableReference variable, object variableValue, bool first) { RenderElement(builder, variable, variableValue, first, true); }
private static string BuildVariablePattern(VariableReference variable, bool allowReservedSet, string groupName, ICollection <string> requiredVariables, ICollection <string> arrayVariables, ICollection <string> mapVariables) { string characterPattern; if (allowReservedSet) { characterPattern = "(?:" + UnreservedCharacterPattern + "|" + ReservedCharacterPattern + ")"; } else { characterPattern = "(?:" + UnreservedCharacterPattern + ")"; } string valueStartPattern; if (!string.IsNullOrEmpty(groupName)) { valueStartPattern = "(?<" + groupName + ">"; } else { valueStartPattern = "(?:"; } string valueEndPattern = ")"; string nameStartPattern; if (!string.IsNullOrEmpty(groupName)) { nameStartPattern = "(?<" + groupName + "name>"; } else { nameStartPattern = "(?:"; } string nameEndPattern = ")"; string keyStartPattern; if (!string.IsNullOrEmpty(groupName)) { keyStartPattern = "(?<" + groupName + "key>"; } else { keyStartPattern = "(?:"; } string keyEndPattern = ")"; string mapValueStartPattern; if (!string.IsNullOrEmpty(groupName)) { mapValueStartPattern = "(?<" + groupName + "value>"; } else { mapValueStartPattern = "(?:"; } string mapValueEndPattern = ")"; string countPattern; if (allowReservedSet) { countPattern = "*?"; } else { countPattern = "*"; } string positiveCountPattern; if (allowReservedSet) { positiveCountPattern = "+?"; } else { positiveCountPattern = "+"; } StringBuilder variablePattern = new StringBuilder(); if (variable.Prefix != null) { // by this point we know to match the variable as a simple string variablePattern.Append("(?:"); variablePattern.Append(nameStartPattern).Append(Regex.Escape(variable.Name)).Append(nameEndPattern); // the '=' is only included for non-empty values variablePattern.Append("(?:="); variablePattern.Append(valueStartPattern); variablePattern.Append(characterPattern); variablePattern.Append("{1,").Append(variable.Prefix).Append("}"); variablePattern.Append(valueEndPattern); variablePattern.Append("|").Append(valueStartPattern).Append(valueEndPattern); variablePattern.Append(")"); variablePattern.Append(")"); return(variablePattern.ToString()); } bool treatAsArray = arrayVariables.Contains(variable.Name); bool treatAsMap = mapVariables.Contains(variable.Name); bool considerString = !variable.Composite && !treatAsArray && !treatAsMap; bool considerArray = treatAsArray || !treatAsMap; bool considerMap = treatAsMap || !treatAsArray; variablePattern.Append("(?:"); if (considerString) { // could be a simple string variablePattern.Append(nameStartPattern).Append(Regex.Escape(variable.Name)).Append(nameEndPattern); // the '=' is only included for non-empty values variablePattern.Append("(?:="); variablePattern.Append(valueStartPattern); variablePattern.Append(characterPattern).Append(positiveCountPattern); variablePattern.Append(valueEndPattern); variablePattern.Append("|").Append(valueStartPattern).Append(valueEndPattern); variablePattern.Append(")"); } if (considerArray) { if (considerString) { variablePattern.Append('|'); } // could be an associative array variablePattern.Append(nameStartPattern).Append(Regex.Escape(variable.Name)).Append(nameEndPattern); if (variable.Composite) { // the '=' is only included for non-empty values variablePattern.Append("(?:="); variablePattern.Append(valueStartPattern).Append(characterPattern).Append(positiveCountPattern).Append(valueEndPattern); variablePattern.Append("|").Append(valueStartPattern).Append(valueEndPattern); variablePattern.Append(")"); } else { // the '=' is only included for non-empty values variablePattern.Append("(?:"); // positiveCountPattern if only one item variablePattern.Append('='); variablePattern.Append(valueStartPattern).Append(characterPattern).Append(positiveCountPattern).Append(valueEndPattern); // countPattern for multiple items (the ',' will make the value non-empty) variablePattern.Append("|="); variablePattern.Append(valueStartPattern).Append(characterPattern).Append(countPattern).Append(valueEndPattern); variablePattern.Append("(?:,"); variablePattern.Append(valueStartPattern).Append(characterPattern).Append(countPattern).Append(valueEndPattern); variablePattern.Append(")+?"); // zero items variablePattern.Append("|").Append(valueStartPattern).Append(valueEndPattern); variablePattern.Append(")"); } } if (considerMap) { if (considerString || considerArray) { variablePattern.Append('|'); } // could be an associative map if (variable.Composite) { variablePattern.Append(valueStartPattern); variablePattern.Append(keyStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(keyEndPattern); // the '=' is only included for non-empty values variablePattern.Append("(?:="); variablePattern.Append(mapValueStartPattern).Append(characterPattern).Append(positiveCountPattern).Append(mapValueEndPattern); variablePattern.Append("|").Append(mapValueStartPattern).Append(mapValueEndPattern); variablePattern.Append(")"); variablePattern.Append(valueEndPattern); } else { variablePattern.Append(nameStartPattern).Append(Regex.Escape(variable.Name)).Append(nameEndPattern); // the '=' is only included for non-empty values variablePattern.Append("(?:="); // the ',' between the key and value allows countPattern and still never empty variablePattern.Append(valueStartPattern); variablePattern.Append(keyStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(keyEndPattern); variablePattern.Append(',').Append(mapValueStartPattern).Append(characterPattern).Append(countPattern).Append(mapValueEndPattern); variablePattern.Append(valueEndPattern); /* Composite variables appear as separate path parameters that are aggregated by the Match method. * This expression only needs to handle the non-composite case. */ variablePattern.Append("(?:,"); variablePattern.Append(valueStartPattern); variablePattern.Append(keyStartPattern); variablePattern.Append(characterPattern).Append(countPattern); variablePattern.Append(keyEndPattern); variablePattern.Append(',').Append(mapValueStartPattern).Append(characterPattern).Append(countPattern).Append(mapValueEndPattern); variablePattern.Append(valueEndPattern); variablePattern.Append(")*?"); // zero items variablePattern.Append("|").Append(valueStartPattern).Append(valueEndPattern); variablePattern.Append(")"); } } variablePattern.Append(")"); return(variablePattern.ToString()); }
protected override KeyValuePair <VariableReference, object>[] MatchImpl(string text, ICollection <string> requiredVariables, ICollection <string> arrayVariables, ICollection <string> mapVariables) { List <string> variablePatterns = new List <string>(); for (int i = 0; i < Variables.Count; i++) { bool allowReservedSet = false; variablePatterns.Add(BuildVariablePattern(Variables[i], allowReservedSet, "var" + i, requiredVariables, arrayVariables, mapVariables)); } StringBuilder matchPattern = new StringBuilder(); matchPattern.Append("^").Append(Regex.Escape(Type == UriTemplatePartType.QueryContinuation ? "&" : "?")); AppendOneOrMoreUnorderedToEnd(matchPattern, variablePatterns, 0); matchPattern.Append("$"); Match match = Regex.Match(text, matchPattern.ToString()); List <KeyValuePair <VariableReference, object> > results = new List <KeyValuePair <VariableReference, object> >(); for (int i = 0; i < Variables.Count; i++) { VariableReference variable = Variables[i]; Group group = match.Groups["var" + i]; if (!group.Success || group.Captures.Count == 0) { continue; } if (!variable.Composite) { /* &id=x&id=y is only valid for {&id*}; * {&id} would produce &id=x,y instead. */ Group nameGroup = match.Groups["var" + i + "name"]; if (nameGroup.Success && nameGroup.Captures.Count > 1) { return(null); } Debug.Assert(nameGroup.Success && nameGroup.Captures.Count == 1, "nameGroup.Success && nameGroup.Captures.Count == 1"); } if (Variables[i].Prefix != null) { if (group.Success && group.Captures.Count == 1) { results.Add(new KeyValuePair <VariableReference, object>(Variables[i], DecodeCharacters(group.Captures[0].Value))); } continue; } bool treatAsArray = arrayVariables.Contains(Variables[i].Name); bool treatAsMap = mapVariables.Contains(Variables[i].Name); bool considerString = !Variables[i].Composite && !treatAsArray && !treatAsMap; bool considerArray = treatAsArray || !treatAsMap; bool considerMap = treatAsMap || !treatAsArray; // first check for a map Group mapKeys = match.Groups["var" + i + "key"]; if (mapKeys.Success && mapKeys.Captures.Count > 0) { Debug.Assert(considerMap, "considerMap"); Group mapValues = match.Groups["var" + i + "value"]; Dictionary <string, string> map = new Dictionary <string, string>(); for (int j = 0; j < mapKeys.Captures.Count; j++) { map.Add(DecodeCharacters(mapKeys.Captures[j].Value), DecodeCharacters(mapValues.Captures[j].Value)); } results.Add(new KeyValuePair <VariableReference, object>(Variables[i], map)); continue; } // next try an array if (!considerString || group.Captures.Count > 1) { Debug.Assert(considerArray, "considerArray"); List <string> list = new List <string>(group.Captures.Count); foreach (Capture capture in group.Captures) { list.Add(DecodeCharacters(capture.Value)); } results.Add(new KeyValuePair <VariableReference, object>(Variables[i], list)); continue; } Debug.Assert(considerString, "considerString"); results.Add(new KeyValuePair <VariableReference, object>(Variables[i], DecodeCharacters(group.Captures[0].Value))); } return(results.ToArray()); }
private static UriTemplatePart[] ParseTemplate(string template) { List <UriTemplatePart> parts = new List <UriTemplatePart>(); int previousEnd = 0; foreach (Match match in ExpressionExpression.Matches(template)) { if (match.Index > previousEnd) { parts.Add(new UriTemplatePartLiteral(template.Substring(previousEnd, match.Index - previousEnd))); } UriTemplatePartType type = UriTemplatePartType.SimpleStringExpansion; Group op = match.Groups["Operator"]; if (op.Success && op.Length > 0) { switch (op.Value) { case "+": type = UriTemplatePartType.ReservedStringExpansion; break; case "#": type = UriTemplatePartType.FragmentExpansion; break; case ".": type = UriTemplatePartType.LabelExpansion; break; case "/": type = UriTemplatePartType.PathSegments; break; case ";": type = UriTemplatePartType.PathParameters; break; case "?": type = UriTemplatePartType.Query; break; case "&": type = UriTemplatePartType.QueryContinuation; break; case "=": case ",": case "!": case "@": case "|": throw new NotSupportedException(string.Format("Operator is reserved for future expansion: {0}", op.Value)); default: throw new InvalidOperationException("Unreachable"); } } Group variableList = match.Groups["VariableList"]; VariableReference[] variables; if (variableList.Success) { string[] specs = variableList.Value.Split(','); variables = new VariableReference[specs.Length]; for (int i = 0; i < specs.Length; i++) { variables[i] = VariableReference.Parse(specs[i]); } } else { variables = new VariableReference[0]; } UriTemplatePart part; switch (type) { case UriTemplatePartType.SimpleStringExpansion: part = new UriTemplatePartSimpleExpansion(variables, true); break; case UriTemplatePartType.ReservedStringExpansion: part = new UriTemplatePartSimpleExpansion(variables, false); break; case UriTemplatePartType.FragmentExpansion: part = new UriTemplatePartFragmentExpansion(variables); break; case UriTemplatePartType.LabelExpansion: part = new UriTemplatePartLabelExpansion(variables); break; case UriTemplatePartType.PathSegments: part = new UriTemplatePartPathSegmentExpansion(variables); break; case UriTemplatePartType.PathParameters: part = new UriTemplatePartPathParametersExpansion(variables); break; case UriTemplatePartType.Query: part = new UriTemplatePartQueryExpansion(variables, false); break; case UriTemplatePartType.QueryContinuation: part = new UriTemplatePartQueryExpansion(variables, true); break; case UriTemplatePartType.Literal: default: throw new InvalidOperationException("Unreachable"); } parts.Add(part); previousEnd = match.Index + match.Length; } if (previousEnd < template.Length) { parts.Add(new UriTemplatePartLiteral(template.Substring(previousEnd))); } return(parts.ToArray()); }