[InlineData(@"{p1:regex(([{{(])\w+)}", @"regex(([{(])\w+)")]                                      // Not balanced {
        public void Parse_RegularExpressions(string template, string constraint)
        {
            // Arrange
            var builder = RoutePatternBuilder.Create(template);

            builder.AddPathSegmentFromText(
                template,
                RoutePatternPart.CreateParameterFromText(
                    template,
                    "p1",
                    null,
                    RoutePatternParameterKind.Standard,
                    ConstraintReference.CreateFromText(constraint, constraint)));

            var expected = builder.Build();

            // Act
            var actual = RoutePatternParser.Parse(template);

            // Assert
            Assert.Equal <RoutePattern>(expected, actual, new RoutePatternEqualityComparer());
        }
Exemple #2
0
        private static ConstraintParseResults ParseConstraints(
            string parameter,
            int currentIndex,
            int endIndex)
        {
            var constraints = new List <ConstraintReference>();
            var state       = ParseState.Start;
            var startIndex  = currentIndex;

            do
            {
                var currentChar = currentIndex > endIndex ? null : (char?)parameter[currentIndex];
                switch (state)
                {
                case ParseState.Start:
                    switch (currentChar)
                    {
                    case null:
                        state = ParseState.End;
                        break;

                    case ':':
                        state      = ParseState.ParsingName;
                        startIndex = currentIndex + 1;
                        break;

                    case '(':
                        state = ParseState.InsideParenthesis;
                        break;

                    case '=':
                        state = ParseState.End;
                        currentIndex--;
                        break;
                    }
                    break;

                case ParseState.InsideParenthesis:
                    switch (currentChar)
                    {
                    case null:
                        state = ParseState.End;
                        var constraintText = parameter.Substring(startIndex, currentIndex - startIndex);
                        constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                        break;

                    case ')':
                        // Only consume a ')' token if
                        // (a) it is the last token
                        // (b) the next character is the start of the new constraint ':'
                        // (c) the next character is the start of the default value.

                        var nextChar = currentIndex + 1 > endIndex ? null : (char?)parameter[currentIndex + 1];
                        switch (nextChar)
                        {
                        case null:
                            state          = ParseState.End;
                            constraintText = parameter.Substring(startIndex, currentIndex - startIndex + 1);
                            constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                            break;

                        case ':':
                            state          = ParseState.Start;
                            constraintText = parameter.Substring(startIndex, currentIndex - startIndex + 1);
                            constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                            startIndex = currentIndex + 1;
                            break;

                        case '=':
                            state          = ParseState.End;
                            constraintText = parameter.Substring(startIndex, currentIndex - startIndex + 1);
                            constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                            break;
                        }
                        break;

                    case ':':
                    case '=':
                        // In the original implementation, the Regex would've backtracked if it encountered an
                        // unbalanced opening bracket followed by (not necessarily immediatiely) a delimiter.
                        // Simply verifying that the parantheses will eventually be closed should suffice to
                        // determine if the terminator needs to be consumed as part of the current constraint
                        // specification.
                        var indexOfClosingParantheses = parameter.IndexOf(')', currentIndex + 1);
                        if (indexOfClosingParantheses == -1)
                        {
                            constraintText = parameter.Substring(startIndex, currentIndex - startIndex);
                            constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));

                            if (currentChar == ':')
                            {
                                state      = ParseState.ParsingName;
                                startIndex = currentIndex + 1;
                            }
                            else
                            {
                                state = ParseState.End;
                                currentIndex--;
                            }
                        }
                        else
                        {
                            currentIndex = indexOfClosingParantheses;
                        }

                        break;
                    }
                    break;

                case ParseState.ParsingName:
                    switch (currentChar)
                    {
                    case null:
                        state = ParseState.End;
                        var constraintText = parameter.Substring(startIndex, currentIndex - startIndex);
                        constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                        break;

                    case ':':
                        constraintText = parameter.Substring(startIndex, currentIndex - startIndex);
                        constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                        startIndex = currentIndex + 1;
                        break;

                    case '(':
                        state = ParseState.InsideParenthesis;
                        break;

                    case '=':
                        state          = ParseState.End;
                        constraintText = parameter.Substring(startIndex, currentIndex - startIndex);
                        constraints.Add(ConstraintReference.CreateFromText(constraintText, constraintText));
                        currentIndex--;
                        break;
                    }
                    break;
                }

                currentIndex++;
            } while (state != ParseState.End);

            return(new ConstraintParseResults
            {
                CurrentIndex = currentIndex,
                Constraints = constraints
            });
        }