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;
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #5
0
 /// <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);
Beispiel #6
0
 /// <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);
Beispiel #7
0
 /// <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);
 }
Beispiel #11
0
        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());
        }
Beispiel #12
0
        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());
        }