Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor RoutePatternFactory to trim regex dependency where possible #46793

Merged
merged 2 commits into from Feb 22, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
121 changes: 65 additions & 56 deletions src/Http/Routing/src/Patterns/RoutePatternFactory.cs
Expand Up @@ -58,7 +58,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, object?
ArgumentNullException.ThrowIfNull(pattern);

var original = RoutePatternParser.Parse(pattern);
return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), requiredValues: null, original.PathSegments);
return PatternCore(original.RawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, original.PathSegments);
}

/// <summary>
Expand All @@ -83,7 +83,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa
ArgumentNullException.ThrowIfNull(pattern);

var original = RoutePatternParser.Parse(pattern);
return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues: null, original.PathSegments);
return PatternCore(original.RawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, original.PathSegments);
}

/// <summary>
Expand Down Expand Up @@ -112,7 +112,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, object?
ArgumentNullException.ThrowIfNull(pattern);

var original = RoutePatternParser.Parse(pattern);
return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), Wrap(requiredValues), original.PathSegments);
return PatternCore(original.RawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), Wrap(requiredValues), original.PathSegments);
}

/// <summary>
Expand Down Expand Up @@ -140,7 +140,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa
ArgumentNullException.ThrowIfNull(pattern);

var original = RoutePatternParser.Parse(pattern);
return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues, original.PathSegments);
return PatternCore(original.RawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues, original.PathSegments);
}

/// <summary>
Expand Down Expand Up @@ -193,7 +193,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerable<RoutePatternPath
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
return PatternCore(null, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments);
}

/// <summary>
Expand All @@ -220,7 +220,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerable<RoutePatternPath
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments);
return PatternCore(null, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments);
}

/// <summary>
Expand Down Expand Up @@ -250,7 +250,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerable<RoutePatternPath
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
return PatternCore(rawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments);
}

/// <summary>
Expand Down Expand Up @@ -279,7 +279,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerable<RoutePatternPath
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments);
return PatternCore(rawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments);
}

/// <summary>
Expand Down Expand Up @@ -332,7 +332,7 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
return PatternCore(null, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments);
}

/// <summary>
Expand All @@ -359,7 +359,7 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments);
return PatternCore(null, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments);
}

/// <summary>
Expand Down Expand Up @@ -389,7 +389,7 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
return PatternCore(rawText, Wrap(defaults), CreateRoutePatternPolicyReferences(Wrap(parameterPolicies)), requiredValues: null, segments);
}

/// <summary>
Expand Down Expand Up @@ -418,13 +418,13 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
{
ArgumentNullException.ThrowIfNull(segments);

return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments);
return PatternCore(rawText, defaults, CreateRoutePatternPolicyReferences(parameterPolicies), requiredValues: null, segments);
}

private static RoutePattern PatternCore(
string? rawText,
RouteValueDictionary? defaults,
RouteValueDictionary? parameterPolicies,
Dictionary<string, List<RoutePatternParameterPolicyReference>>? parameterPolicyReferences,
RouteValueDictionary? requiredValues,
IEnumerable<RoutePatternPathSegment> segments)
{
Expand All @@ -450,43 +450,6 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
}
}

Dictionary<string, List<RoutePatternParameterPolicyReference>>? updatedParameterPolicies = null;
if (parameterPolicies != null && parameterPolicies.Count > 0)
{
updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(parameterPolicies.Count, StringComparer.OrdinalIgnoreCase);

foreach (var kvp in parameterPolicies)
{
var policyReferences = new List<RoutePatternParameterPolicyReference>();

if (kvp.Value is IParameterPolicy parameterPolicy)
{
policyReferences.Add(ParameterPolicy(parameterPolicy));
}
else if (kvp.Value is string)
{
// Constraint will convert string values into regex constraints
policyReferences.Add(Constraint(kvp.Value));
}
else if (kvp.Value is IEnumerable multiplePolicies)
{
foreach (var item in multiplePolicies)
{
// Constraint will convert string values into regex constraints
policyReferences.Add(item is IParameterPolicy p ? ParameterPolicy(p) : Constraint(item));
}
}
else
{
throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference(
kvp.Value ?? "null",
typeof(IRouteConstraint)));
}

updatedParameterPolicies.Add(kvp.Key, policyReferences);
}
}

List<RoutePatternParameterPart>? parameters = null;
var updatedSegments = segments.ToArray();
for (var i = 0; i < updatedSegments.Length; i++)
Expand Down Expand Up @@ -554,8 +517,8 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
return new RoutePattern(
rawText,
updatedDefaults ?? EmptyDictionary,
updatedParameterPolicies != null
? updatedParameterPolicies.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<RoutePatternParameterPolicyReference>)kvp.Value.ToArray())
parameterPolicyReferences != null
? parameterPolicyReferences.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList<RoutePatternParameterPolicyReference>)kvp.Value.ToArray())
: EmptyPoliciesDictionary,
requiredValues ?? EmptyDictionary,
(IReadOnlyList<RoutePatternParameterPart>?)parameters ?? Array.Empty<RoutePatternParameterPart>(),
Expand Down Expand Up @@ -627,16 +590,16 @@ RoutePatternPart VisitPart(RoutePatternPart part)
}

List<RoutePatternParameterPolicyReference>? parameterConstraints = null;
if ((updatedParameterPolicies == null || !updatedParameterPolicies.TryGetValue(parameter.Name, out parameterConstraints)) &&
if ((parameterPolicyReferences == null || !parameterPolicyReferences.TryGetValue(parameter.Name, out parameterConstraints)) &&
parameter.ParameterPolicies.Count > 0)
{
if (updatedParameterPolicies == null)
if (parameterPolicyReferences == null)
{
updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(StringComparer.OrdinalIgnoreCase);
parameterPolicyReferences = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(StringComparer.OrdinalIgnoreCase);
}

parameterConstraints = new List<RoutePatternParameterPolicyReference>(parameter.ParameterPolicies.Count);
updatedParameterPolicies.Add(parameter.Name, parameterConstraints);
parameterPolicyReferences.Add(parameter.Name, parameterConstraints);
}

if (parameter.ParameterPolicies.Count > 0)
Expand All @@ -661,6 +624,52 @@ RoutePatternPart VisitPart(RoutePatternPart part)
}
}

/// <summary>
/// String policy references are infered to be regex constraints. Creating them is moved here to its own method so apps can
/// trim away the regex dependency when RoutePatternFactory.Parse(string) is used. This is the method typically used by the various Map methods.
/// </summary>
private static Dictionary<string, List<RoutePatternParameterPolicyReference>>? CreateRoutePatternPolicyReferences(RouteValueDictionary? parameterPolicies)
{
Dictionary<string, List<RoutePatternParameterPolicyReference>>? updatedParameterPolicies = null;
if (parameterPolicies != null && parameterPolicies.Count > 0)
{
updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(parameterPolicies.Count, StringComparer.OrdinalIgnoreCase);

foreach (var kvp in parameterPolicies)
{
var policyReferences = new List<RoutePatternParameterPolicyReference>();

if (kvp.Value is IParameterPolicy parameterPolicy)
{
policyReferences.Add(ParameterPolicy(parameterPolicy));
}
else if (kvp.Value is string)
{
// Constraint will convert string values into regex constraints
policyReferences.Add(Constraint(kvp.Value));
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
}
else if (kvp.Value is IEnumerable multiplePolicies)
{
foreach (var item in multiplePolicies)
{
// Constraint will convert string values into regex constraints
policyReferences.Add(item is IParameterPolicy p ? ParameterPolicy(p) : Constraint(item));
}
}
else
{
throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference(
kvp.Value ?? "null",
typeof(IRouteConstraint)));
}

updatedParameterPolicies.Add(kvp.Key, policyReferences);
}
}

return updatedParameterPolicies;
}

/// <summary>
/// Creates a <see cref="RoutePatternPathSegment"/> from the provided collection
/// of parts.
Expand Down