Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Http/Routing/src/Patterns/RoutePatternParser.cs')
-rw-r--r--src/Http/Routing/src/Patterns/RoutePatternParser.cs809
1 files changed, 404 insertions, 405 deletions
diff --git a/src/Http/Routing/src/Patterns/RoutePatternParser.cs b/src/Http/Routing/src/Patterns/RoutePatternParser.cs
index 26e9f69b4a..8b3e4760ec 100644
--- a/src/Http/Routing/src/Patterns/RoutePatternParser.cs
+++ b/src/Http/Routing/src/Patterns/RoutePatternParser.cs
@@ -8,568 +8,567 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
-namespace Microsoft.AspNetCore.Routing.Patterns
+namespace Microsoft.AspNetCore.Routing.Patterns;
+
+internal static class RoutePatternParser
{
- internal static class RoutePatternParser
+ private const char Separator = '/';
+ private const char OpenBrace = '{';
+ private const char CloseBrace = '}';
+ private const char QuestionMark = '?';
+ private const char Asterisk = '*';
+ private const string PeriodString = ".";
+
+ internal static readonly char[] InvalidParameterNameChars = new char[]
{
- private const char Separator = '/';
- private const char OpenBrace = '{';
- private const char CloseBrace = '}';
- private const char QuestionMark = '?';
- private const char Asterisk = '*';
- private const string PeriodString = ".";
-
- internal static readonly char[] InvalidParameterNameChars = new char[]
- {
Separator,
OpenBrace,
CloseBrace,
QuestionMark,
Asterisk
- };
+ };
- public static RoutePattern Parse(string pattern)
+ public static RoutePattern Parse(string pattern)
+ {
+ if (pattern == null)
{
- if (pattern == null)
- {
- throw new ArgumentNullException(nameof(pattern));
- }
-
- var trimmedPattern = TrimPrefix(pattern);
-
- var context = new Context(trimmedPattern);
- var segments = new List<RoutePatternPathSegment>();
-
- while (context.MoveNext())
- {
- var i = context.Index;
+ throw new ArgumentNullException(nameof(pattern));
+ }
- if (context.Current == Separator)
- {
- // If we get here is means that there's a consecutive '/' character.
- // Templates don't start with a '/' and parsing a segment consumes the separator.
- throw new RoutePatternException(pattern, Resources.TemplateRoute_CannotHaveConsecutiveSeparators);
- }
+ var trimmedPattern = TrimPrefix(pattern);
- if (!ParseSegment(context, segments))
- {
- throw new RoutePatternException(pattern, context.Error);
- }
+ var context = new Context(trimmedPattern);
+ var segments = new List<RoutePatternPathSegment>();
- // A successful parse should always result in us being at the end or at a separator.
- Debug.Assert(context.AtEnd() || context.Current == Separator);
+ while (context.MoveNext())
+ {
+ var i = context.Index;
- if (context.Index <= i)
- {
- // This shouldn't happen, but we want to crash if it does.
- var message = "Infinite loop detected in the parser. Please open an issue.";
- throw new InvalidProgramException(message);
- }
+ if (context.Current == Separator)
+ {
+ // If we get here is means that there's a consecutive '/' character.
+ // Templates don't start with a '/' and parsing a segment consumes the separator.
+ throw new RoutePatternException(pattern, Resources.TemplateRoute_CannotHaveConsecutiveSeparators);
}
- if (IsAllValid(context, segments))
+ if (!ParseSegment(context, segments))
{
- return RoutePatternFactory.Pattern(pattern, segments);
+ throw new RoutePatternException(pattern, context.Error);
}
- else
+
+ // A successful parse should always result in us being at the end or at a separator.
+ Debug.Assert(context.AtEnd() || context.Current == Separator);
+
+ if (context.Index <= i)
{
- throw new RoutePatternException(pattern, context.Error);
+ // This shouldn't happen, but we want to crash if it does.
+ var message = "Infinite loop detected in the parser. Please open an issue.";
+ throw new InvalidProgramException(message);
}
}
- private static bool ParseSegment(Context context, List<RoutePatternPathSegment> segments)
+ if (IsAllValid(context, segments))
+ {
+ return RoutePatternFactory.Pattern(pattern, segments);
+ }
+ else
{
- Debug.Assert(context != null);
- Debug.Assert(segments != null);
+ throw new RoutePatternException(pattern, context.Error);
+ }
+ }
+
+ private static bool ParseSegment(Context context, List<RoutePatternPathSegment> segments)
+ {
+ Debug.Assert(context != null);
+ Debug.Assert(segments != null);
- var parts = new List<RoutePatternPart>();
+ var parts = new List<RoutePatternPart>();
+
+ while (true)
+ {
+ var i = context.Index;
- while (true)
+ if (context.Current == OpenBrace)
{
- var i = context.Index;
+ if (!context.MoveNext())
+ {
+ // This is a dangling open-brace, which is not allowed
+ context.Error = Resources.TemplateRoute_MismatchedParameter;
+ return false;
+ }
if (context.Current == OpenBrace)
{
- if (!context.MoveNext())
+ // This is an 'escaped' brace in a literal, like "{{foo"
+ context.Back();
+ if (!ParseLiteral(context, parts))
{
- // This is a dangling open-brace, which is not allowed
- context.Error = Resources.TemplateRoute_MismatchedParameter;
return false;
}
-
- if (context.Current == OpenBrace)
- {
- // This is an 'escaped' brace in a literal, like "{{foo"
- context.Back();
- if (!ParseLiteral(context, parts))
- {
- return false;
- }
- }
- else
- {
- // This is a parameter
- context.Back();
- if (!ParseParameter(context, parts))
- {
- return false;
- }
- }
}
else
{
- if (!ParseLiteral(context, parts))
+ // This is a parameter
+ context.Back();
+ if (!ParseParameter(context, parts))
{
return false;
}
}
-
- if (context.Current == Separator || context.AtEnd())
- {
- // We've reached the end of the segment
- break;
- }
-
- if (context.Index <= i)
+ }
+ else
+ {
+ if (!ParseLiteral(context, parts))
{
- // This shouldn't happen, but we want to crash if it does.
- var message = "Infinite loop detected in the parser. Please open an issue.";
- throw new InvalidProgramException(message);
+ return false;
}
}
- if (IsSegmentValid(context, parts))
+ if (context.Current == Separator || context.AtEnd())
{
- segments.Add(new RoutePatternPathSegment(parts));
- return true;
+ // We've reached the end of the segment
+ break;
}
- else
+
+ if (context.Index <= i)
{
- return false;
+ // This shouldn't happen, but we want to crash if it does.
+ var message = "Infinite loop detected in the parser. Please open an issue.";
+ throw new InvalidProgramException(message);
}
}
- private static bool ParseParameter(Context context, List<RoutePatternPart> parts)
+ if (IsSegmentValid(context, parts))
{
- Debug.Assert(context.Current == OpenBrace);
- context.Mark();
+ segments.Add(new RoutePatternPathSegment(parts));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private static bool ParseParameter(Context context, List<RoutePatternPart> parts)
+ {
+ Debug.Assert(context.Current == OpenBrace);
+ context.Mark();
- context.MoveNext();
+ context.MoveNext();
- while (true)
+ while (true)
+ {
+ if (context.Current == OpenBrace)
{
- if (context.Current == OpenBrace)
+ // This is an open brace inside of a parameter, it has to be escaped
+ if (context.MoveNext())
{
- // This is an open brace inside of a parameter, it has to be escaped
- if (context.MoveNext())
- {
- if (context.Current != OpenBrace)
- {
- // If we see something like "{p1:regex(^\d{3", we will come here.
- context.Error = Resources.TemplateRoute_UnescapedBrace;
- return false;
- }
- }
- else
+ if (context.Current != OpenBrace)
{
- // This is a dangling open-brace, which is not allowed
- // Example: "{p1:regex(^\d{"
- context.Error = Resources.TemplateRoute_MismatchedParameter;
+ // If we see something like "{p1:regex(^\d{3", we will come here.
+ context.Error = Resources.TemplateRoute_UnescapedBrace;
return false;
}
}
- else if (context.Current == CloseBrace)
- {
- // When we encounter Closed brace here, it either means end of the parameter or it is a closed
- // brace in the parameter, in that case it needs to be escaped.
- // Example: {p1:regex(([}}])\w+}. First pair is escaped one and last marks end of the parameter
- if (!context.MoveNext())
- {
- // This is the end of the string -and we have a valid parameter
- break;
- }
-
- if (context.Current == CloseBrace)
- {
- // This is an 'escaped' brace in a parameter name
- }
- else
- {
- // This is the end of the parameter
- break;
- }
- }
-
- if (!context.MoveNext())
+ else
{
// This is a dangling open-brace, which is not allowed
+ // Example: "{p1:regex(^\d{"
context.Error = Resources.TemplateRoute_MismatchedParameter;
return false;
}
}
+ else if (context.Current == CloseBrace)
+ {
+ // When we encounter Closed brace here, it either means end of the parameter or it is a closed
+ // brace in the parameter, in that case it needs to be escaped.
+ // Example: {p1:regex(([}}])\w+}. First pair is escaped one and last marks end of the parameter
+ if (!context.MoveNext())
+ {
+ // This is the end of the string -and we have a valid parameter
+ break;
+ }
+
+ if (context.Current == CloseBrace)
+ {
+ // This is an 'escaped' brace in a parameter name
+ }
+ else
+ {
+ // This is the end of the parameter
+ break;
+ }
+ }
- var text = context.Capture();
- if (text == "{}")
+ if (!context.MoveNext())
{
- context.Error = Resources.FormatTemplateRoute_InvalidParameterName(string.Empty);
+ // This is a dangling open-brace, which is not allowed
+ context.Error = Resources.TemplateRoute_MismatchedParameter;
return false;
}
+ }
- var inside = text.Substring(1, text.Length - 2);
- var decoded = inside.Replace("}}", "}").Replace("{{", "{");
+ var text = context.Capture();
+ if (text == "{}")
+ {
+ context.Error = Resources.FormatTemplateRoute_InvalidParameterName(string.Empty);
+ return false;
+ }
- // At this point, we need to parse the raw name for inline constraint,
- // default values and optional parameters.
- var templatePart = RouteParameterParser.ParseRouteParameter(decoded);
+ var inside = text.Substring(1, text.Length - 2);
+ var decoded = inside.Replace("}}", "}").Replace("{{", "{");
- // See #475 - this is here because InlineRouteParameterParser can't return errors
- if (decoded.StartsWith("*", StringComparison.Ordinal) && decoded.EndsWith("?", StringComparison.Ordinal))
- {
- context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional;
- return false;
- }
+ // At this point, we need to parse the raw name for inline constraint,
+ // default values and optional parameters.
+ var templatePart = RouteParameterParser.ParseRouteParameter(decoded);
- if (templatePart.IsOptional && templatePart.Default != null)
- {
- // Cannot be optional and have a default value.
- // The only way to declare an optional parameter is to have a ? at the end,
- // hence we cannot have both default value and optional parameter within the template.
- // A workaround is to add it as a separate entry in the defaults argument.
- context.Error = Resources.TemplateRoute_OptionalCannotHaveDefaultValue;
- return false;
- }
+ // See #475 - this is here because InlineRouteParameterParser can't return errors
+ if (decoded.StartsWith("*", StringComparison.Ordinal) && decoded.EndsWith("?", StringComparison.Ordinal))
+ {
+ context.Error = Resources.TemplateRoute_CatchAllCannotBeOptional;
+ return false;
+ }
- var parameterName = templatePart.Name;
- if (IsValidParameterName(context, parameterName))
- {
- parts.Add(templatePart);
- return true;
- }
- else
- {
- return false;
- }
+ if (templatePart.IsOptional && templatePart.Default != null)
+ {
+ // Cannot be optional and have a default value.
+ // The only way to declare an optional parameter is to have a ? at the end,
+ // hence we cannot have both default value and optional parameter within the template.
+ // A workaround is to add it as a separate entry in the defaults argument.
+ context.Error = Resources.TemplateRoute_OptionalCannotHaveDefaultValue;
+ return false;
}
- private static bool ParseLiteral(Context context, List<RoutePatternPart> parts)
+ var parameterName = templatePart.Name;
+ if (IsValidParameterName(context, parameterName))
{
- context.Mark();
+ parts.Add(templatePart);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private static bool ParseLiteral(Context context, List<RoutePatternPart> parts)
+ {
+ context.Mark();
- while (true)
+ while (true)
+ {
+ if (context.Current == Separator)
{
- if (context.Current == Separator)
+ // End of the segment
+ break;
+ }
+ else if (context.Current == OpenBrace)
+ {
+ if (!context.MoveNext())
{
- // End of the segment
- break;
+ // This is a dangling open-brace, which is not allowed
+ context.Error = Resources.TemplateRoute_MismatchedParameter;
+ return false;
}
- else if (context.Current == OpenBrace)
- {
- if (!context.MoveNext())
- {
- // This is a dangling open-brace, which is not allowed
- context.Error = Resources.TemplateRoute_MismatchedParameter;
- return false;
- }
- if (context.Current == OpenBrace)
- {
- // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
- }
- else
- {
- // We've just seen the start of a parameter, so back up.
- context.Back();
- break;
- }
- }
- else if (context.Current == CloseBrace)
+ if (context.Current == OpenBrace)
{
- if (!context.MoveNext())
- {
- // This is a dangling close-brace, which is not allowed
- context.Error = Resources.TemplateRoute_MismatchedParameter;
- return false;
- }
-
- if (context.Current == CloseBrace)
- {
- // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
- }
- else
- {
- // This is an unbalanced close-brace, which is not allowed
- context.Error = Resources.TemplateRoute_MismatchedParameter;
- return false;
- }
+ // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
}
-
- if (!context.MoveNext())
+ else
{
+ // We've just seen the start of a parameter, so back up.
+ context.Back();
break;
}
}
-
- var encoded = context.Capture();
- var decoded = encoded.Replace("}}", "}").Replace("{{", "{");
- if (IsValidLiteral(context, decoded))
+ else if (context.Current == CloseBrace)
{
- parts.Add(RoutePatternFactory.LiteralPart(decoded));
- return true;
+ if (!context.MoveNext())
+ {
+ // This is a dangling close-brace, which is not allowed
+ context.Error = Resources.TemplateRoute_MismatchedParameter;
+ return false;
+ }
+
+ if (context.Current == CloseBrace)
+ {
+ // This is an 'escaped' brace in a literal, like "{{foo" - keep going.
+ }
+ else
+ {
+ // This is an unbalanced close-brace, which is not allowed
+ context.Error = Resources.TemplateRoute_MismatchedParameter;
+ return false;
+ }
}
- else
+
+ if (!context.MoveNext())
{
- return false;
+ break;
}
}
- private static bool IsAllValid(Context context, List<RoutePatternPathSegment> segments)
+ var encoded = context.Capture();
+ var decoded = encoded.Replace("}}", "}").Replace("{{", "{");
+ if (IsValidLiteral(context, decoded))
{
- // A catch-all parameter must be the last part of the last segment
- for (var i = 0; i < segments.Count; i++)
- {
- var segment = segments[i];
- for (var j = 0; j < segment.Parts.Count; j++)
- {
- var part = segment.Parts[j];
- if (part is RoutePatternParameterPart parameter
- && parameter.IsCatchAll &&
- (i != segments.Count - 1 || j != segment.Parts.Count - 1))
- {
- context.Error = Resources.TemplateRoute_CatchAllMustBeLast;
- return false;
- }
- }
- }
-
+ parts.Add(RoutePatternFactory.LiteralPart(decoded));
return true;
}
+ else
+ {
+ return false;
+ }
+ }
- private static bool IsSegmentValid(Context context, List<RoutePatternPart> parts)
+ private static bool IsAllValid(Context context, List<RoutePatternPathSegment> segments)
+ {
+ // A catch-all parameter must be the last part of the last segment
+ for (var i = 0; i < segments.Count; i++)
{
- // If a segment has multiple parts, then it can't contain a catch all.
- for (var i = 0; i < parts.Count; i++)
+ var segment = segments[i];
+ for (var j = 0; j < segment.Parts.Count; j++)
{
- var part = parts[i];
- if (part is RoutePatternParameterPart parameter && parameter.IsCatchAll && parts.Count > 1)
+ var part = segment.Parts[j];
+ if (part is RoutePatternParameterPart parameter
+ && parameter.IsCatchAll &&
+ (i != segments.Count - 1 || j != segment.Parts.Count - 1))
{
- context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
+ context.Error = Resources.TemplateRoute_CatchAllMustBeLast;
return false;
}
}
+ }
- // if a segment has multiple parts, then only the last one parameter can be optional
- // if it is following a optional separator.
- for (var i = 0; i < parts.Count; i++)
+ return true;
+ }
+
+ private static bool IsSegmentValid(Context context, List<RoutePatternPart> parts)
+ {
+ // If a segment has multiple parts, then it can't contain a catch all.
+ for (var i = 0; i < parts.Count; i++)
+ {
+ var part = parts[i];
+ if (part is RoutePatternParameterPart parameter && parameter.IsCatchAll && parts.Count > 1)
{
- var part = parts[i];
+ context.Error = Resources.TemplateRoute_CannotHaveCatchAllInMultiSegment;
+ return false;
+ }
+ }
+
+ // if a segment has multiple parts, then only the last one parameter can be optional
+ // if it is following a optional separator.
+ for (var i = 0; i < parts.Count; i++)
+ {
+ var part = parts[i];
- if (part is RoutePatternParameterPart parameter && parameter.IsOptional && parts.Count > 1)
+ if (part is RoutePatternParameterPart parameter && parameter.IsOptional && parts.Count > 1)
+ {
+ // This optional parameter is the last part in the segment
+ if (i == parts.Count - 1)
{
- // This optional parameter is the last part in the segment
- if (i == parts.Count - 1)
+ var previousPart = parts[i - 1];
+
+ if (!previousPart.IsLiteral && !previousPart.IsSeparator)
{
- var previousPart = parts[i - 1];
-
- if (!previousPart.IsLiteral && !previousPart.IsSeparator)
- {
- // The optional parameter is preceded by something that is not a literal or separator
- // Example of error message:
- // "In the segment '{RouteValue}{param?}', the optional parameter 'param' is preceded
- // by an invalid segment '{RouteValue}'. Only a period (.) can precede an optional parameter.
- context.Error = Resources.FormatTemplateRoute_OptionalParameterCanbBePrecededByPeriod(
- RoutePatternPathSegment.DebuggerToString(parts),
- parameter.Name,
- parts[i - 1].DebuggerToString());
-
- return false;
- }
- else if (previousPart is RoutePatternLiteralPart literal && literal.Content != PeriodString)
- {
- // The optional parameter is preceded by a literal other than period.
- // Example of error message:
- // "In the segment '{RouteValue}-{param?}', the optional parameter 'param' is preceded
- // by an invalid segment '-'. Only a period (.) can precede an optional parameter.
- context.Error = Resources.FormatTemplateRoute_OptionalParameterCanbBePrecededByPeriod(
- RoutePatternPathSegment.DebuggerToString(parts),
- parameter.Name,
- parts[i - 1].DebuggerToString());
-
- return false;
- }
-
- parts[i - 1] = RoutePatternFactory.SeparatorPart(((RoutePatternLiteralPart)previousPart).Content);
+ // The optional parameter is preceded by something that is not a literal or separator
+ // Example of error message:
+ // "In the segment '{RouteValue}{param?}', the optional parameter 'param' is preceded
+ // by an invalid segment '{RouteValue}'. Only a period (.) can precede an optional parameter.
+ context.Error = Resources.FormatTemplateRoute_OptionalParameterCanbBePrecededByPeriod(
+ RoutePatternPathSegment.DebuggerToString(parts),
+ parameter.Name,
+ parts[i - 1].DebuggerToString());
+
+ return false;
}
- else
+ else if (previousPart is RoutePatternLiteralPart literal && literal.Content != PeriodString)
{
- // This optional parameter is not the last one in the segment
- // Example:
- // An optional parameter must be at the end of the segment. In the segment '{RouteValue?})',
- // optional parameter 'RouteValue' is followed by ')'
- context.Error = Resources.FormatTemplateRoute_OptionalParameterHasTobeTheLast(
+ // The optional parameter is preceded by a literal other than period.
+ // Example of error message:
+ // "In the segment '{RouteValue}-{param?}', the optional parameter 'param' is preceded
+ // by an invalid segment '-'. Only a period (.) can precede an optional parameter.
+ context.Error = Resources.FormatTemplateRoute_OptionalParameterCanbBePrecededByPeriod(
RoutePatternPathSegment.DebuggerToString(parts),
parameter.Name,
- parts[i + 1].DebuggerToString());
+ parts[i - 1].DebuggerToString());
return false;
}
- }
- }
- // A segment cannot contain two consecutive parameters
- var isLastSegmentParameter = false;
- for (var i = 0; i < parts.Count; i++)
- {
- var part = parts[i];
- if (part.IsParameter && isLastSegmentParameter)
+ parts[i - 1] = RoutePatternFactory.SeparatorPart(((RoutePatternLiteralPart)previousPart).Content);
+ }
+ else
{
- context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
+ // This optional parameter is not the last one in the segment
+ // Example:
+ // An optional parameter must be at the end of the segment. In the segment '{RouteValue?})',
+ // optional parameter 'RouteValue' is followed by ')'
+ context.Error = Resources.FormatTemplateRoute_OptionalParameterHasTobeTheLast(
+ RoutePatternPathSegment.DebuggerToString(parts),
+ parameter.Name,
+ parts[i + 1].DebuggerToString());
+
return false;
}
-
- isLastSegmentParameter = part.IsParameter;
}
-
- return true;
}
- private static bool IsValidParameterName(Context context, string parameterName)
+ // A segment cannot contain two consecutive parameters
+ var isLastSegmentParameter = false;
+ for (var i = 0; i < parts.Count; i++)
{
- if (parameterName.Length == 0 || parameterName.IndexOfAny(InvalidParameterNameChars) >= 0)
+ var part = parts[i];
+ if (part.IsParameter && isLastSegmentParameter)
{
- context.Error = Resources.FormatTemplateRoute_InvalidParameterName(parameterName);
+ context.Error = Resources.TemplateRoute_CannotHaveConsecutiveParameters;
return false;
}
- if (!context.ParameterNames.Add(parameterName))
- {
- context.Error = Resources.FormatTemplateRoute_RepeatedParameter(parameterName);
- return false;
- }
+ isLastSegmentParameter = part.IsParameter;
+ }
- return true;
+ return true;
+ }
+
+ private static bool IsValidParameterName(Context context, string parameterName)
+ {
+ if (parameterName.Length == 0 || parameterName.IndexOfAny(InvalidParameterNameChars) >= 0)
+ {
+ context.Error = Resources.FormatTemplateRoute_InvalidParameterName(parameterName);
+ return false;
}
- private static bool IsValidLiteral(Context context, string literal)
+ if (!context.ParameterNames.Add(parameterName))
{
- Debug.Assert(context != null);
- Debug.Assert(literal != null);
+ context.Error = Resources.FormatTemplateRoute_RepeatedParameter(parameterName);
+ return false;
+ }
- if (literal.IndexOf(QuestionMark) != -1)
- {
- context.Error = Resources.FormatTemplateRoute_InvalidLiteral(literal);
- return false;
- }
+ return true;
+ }
- return true;
+ private static bool IsValidLiteral(Context context, string literal)
+ {
+ Debug.Assert(context != null);
+ Debug.Assert(literal != null);
+
+ if (literal.IndexOf(QuestionMark) != -1)
+ {
+ context.Error = Resources.FormatTemplateRoute_InvalidLiteral(literal);
+ return false;
}
- private static string TrimPrefix(string routePattern)
+ return true;
+ }
+
+ private static string TrimPrefix(string routePattern)
+ {
+ if (routePattern.StartsWith("~/", StringComparison.Ordinal))
{
- if (routePattern.StartsWith("~/", StringComparison.Ordinal))
- {
- return routePattern.Substring(2);
- }
- else if (routePattern.StartsWith("/", StringComparison.Ordinal))
- {
- return routePattern.Substring(1);
- }
- else if (routePattern.StartsWith("~", StringComparison.Ordinal))
- {
- throw new RoutePatternException(routePattern, Resources.TemplateRoute_InvalidRouteTemplate);
- }
- return routePattern;
+ return routePattern.Substring(2);
+ }
+ else if (routePattern.StartsWith("/", StringComparison.Ordinal))
+ {
+ return routePattern.Substring(1);
+ }
+ else if (routePattern.StartsWith("~", StringComparison.Ordinal))
+ {
+ throw new RoutePatternException(routePattern, Resources.TemplateRoute_InvalidRouteTemplate);
}
+ return routePattern;
+ }
+
+ [DebuggerDisplay("{DebuggerToString()}")]
+ private class Context
+ {
+ private readonly string _template;
+ private int _index;
+ private int? _mark;
+
+ private readonly HashSet<string> _parameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
- [DebuggerDisplay("{DebuggerToString()}")]
- private class Context
+ public Context(string template)
{
- private readonly string _template;
- private int _index;
- private int? _mark;
+ Debug.Assert(template != null);
+ _template = template;
- private readonly HashSet<string> _parameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ _index = -1;
+ }
- public Context(string template)
- {
- Debug.Assert(template != null);
- _template = template;
+ public char Current
+ {
+ get { return (_index < _template.Length && _index >= 0) ? _template[_index] : (char)0; }
+ }
- _index = -1;
- }
+ public int Index => _index;
- public char Current
- {
- get { return (_index < _template.Length && _index >= 0) ? _template[_index] : (char)0; }
- }
+ public string Error
+ {
+ get;
+ set;
+ }
- public int Index => _index;
+ public HashSet<string> ParameterNames
+ {
+ get { return _parameterNames; }
+ }
- public string Error
- {
- get;
- set;
- }
+ public bool Back()
+ {
+ return --_index >= 0;
+ }
- public HashSet<string> ParameterNames
- {
- get { return _parameterNames; }
- }
+ public bool AtEnd()
+ {
+ return _index >= _template.Length;
+ }
- public bool Back()
- {
- return --_index >= 0;
- }
+ public bool MoveNext()
+ {
+ return ++_index < _template.Length;
+ }
+
+ public void Mark()
+ {
+ Debug.Assert(_index >= 0);
+
+ // Index is always the index of the character *past* Current - we want to 'mark' Current.
+ _mark = _index;
+ }
- public bool AtEnd()
+ public string Capture()
+ {
+ if (_mark.HasValue)
{
- return _index >= _template.Length;
+ var value = _template.Substring(_mark.Value, _index - _mark.Value);
+ _mark = null;
+ return value;
}
-
- public bool MoveNext()
+ else
{
- return ++_index < _template.Length;
+ return null;
}
+ }
- public void Mark()
+ private string DebuggerToString()
+ {
+ if (_index == -1)
{
- Debug.Assert(_index >= 0);
-
- // Index is always the index of the character *past* Current - we want to 'mark' Current.
- _mark = _index;
+ return _template;
}
-
- public string Capture()
+ else if (_mark.HasValue)
{
- if (_mark.HasValue)
- {
- var value = _template.Substring(_mark.Value, _index - _mark.Value);
- _mark = null;
- return value;
- }
- else
- {
- return null;
- }
+ return _template.Substring(0, _mark.Value) +
+ "|" +
+ _template.Substring(_mark.Value, _index - _mark.Value) +
+ "|" +
+ _template.Substring(_index);
}
-
- private string DebuggerToString()
+ else
{
- if (_index == -1)
- {
- return _template;
- }
- else if (_mark.HasValue)
- {
- return _template.Substring(0, _mark.Value) +
- "|" +
- _template.Substring(_mark.Value, _index - _mark.Value) +
- "|" +
- _template.Substring(_index);
- }
- else
- {
- return string.Concat(_template.Substring(0, _index), "|", _template.Substring(_index));
- }
+ return string.Concat(_template.Substring(0, _index), "|", _template.Substring(_index));
}
}
}