/// <summary>Creates a route builder that can build a route matching this context.</summary> /// <param name="template">The route template.</param> /// <param name="constraintResolver"> /// The inline constraint resolver to use, if any; otherwise, <see langword="null"/>. /// </param> /// <returns>A route builder that can build a route matching this context.</returns> public IDirectRouteBuilder CreateBuilder(string template, IInlineConstraintResolver constraintResolver) { DirectRouteBuilder builder = new DirectRouteBuilder(_actions, _targetIsAction); #if ASPNETWEBAPI string prefixedTemplate = BuildRouteTemplate(_prefix, template); #else string prefixedTemplate = BuildRouteTemplate(_areaPrefix, _controllerPrefix, template ?? String.Empty); #endif ValidateTemplate(prefixedTemplate); if (constraintResolver != null) { TRouteDictionary defaults = new TRouteDictionary(); TRouteDictionary constraints = new TRouteDictionary(); string detokenizedTemplate = InlineRouteTemplateParser.ParseRouteTemplate(prefixedTemplate, defaults, constraints, constraintResolver); TParsedRoute parsedRoute = RouteParser.Parse(detokenizedTemplate); decimal precedence = RoutePrecedence.Compute(parsedRoute, constraints); builder.Defaults = defaults; builder.Constraints = constraints; builder.Template = detokenizedTemplate; builder.Precedence = precedence; builder.ParsedRoute = parsedRoute; } else { builder.Template = prefixedTemplate; } return(builder); }
internal virtual DirectRouteBuilder CreateBuilder(string template, IInlineConstraintResolver constraintResolver) { DirectRouteBuilder builder = new DirectRouteBuilder(_actions); string prefixedTemplate = BuildRouteTemplate(_prefix, template); Contract.Assert(prefixedTemplate != null); if (prefixedTemplate.StartsWith("/", StringComparison.Ordinal)) { throw Error.InvalidOperation(SRResources.AttributeRoutes_InvalidTemplate, template, _actionName); } if (constraintResolver != null) { builder.Defaults = new HttpRouteValueDictionary(); builder.Constraints = new HttpRouteValueDictionary(); builder.Template = InlineRouteTemplateParser.ParseRouteTemplate(prefixedTemplate, builder.Defaults, builder.Constraints, constraintResolver); builder.ParsedRoute = HttpRouteParser.Parse(builder.Template); builder.Precedence = RoutePrecedence.Compute(builder.ParsedRoute, builder.Constraints); } else { builder.Template = prefixedTemplate; } return(builder); }
private void ParseEntries(JObject input, List <RouteEntry> entries) { var basePath = ""; if (input["basePath"] is JProperty basePathProperty) { basePath = basePathProperty.Value <string>(); } if (input["paths"] is JObject paths) { foreach (var path in paths.Properties()) { foreach (var method in ((JObject)path.Value).Properties()) { var template = basePath + path.Name; var parsed = TemplateParser.Parse(template); entries.Add(new RouteEntry() { Method = HttpMethods.HasValue() ? method.Name.ToString() : null, Template = parsed, Precedence = RoutePrecedence.ComputeInbound(parsed), }); } } } }
/// <summary> /// Processes one attribute route /// </summary> /// <param name="action">Action to process</param> /// <returns>Route information</returns> private AttributeRouteInfo ProcessAttributeRoute(ControllerActionDescriptor action) { string constraint; action.RouteValues.TryGetValue(TreeRouter.RouteGroupKey, out constraint); if (string.IsNullOrEmpty(constraint)) { // This can happen if an ActionDescriptor has a route template, but doesn't have one of our // special route group constraints. This is a good indication that the user is using a 3rd party // routing system, or has customized their ADs in a way that we can no longer understand them. // // We just treat this case as an 'opt-out' of our attribute routing system. return(null); } var template = TemplateParser.Parse(action.AttributeRouteInfo.Template); var info = new AttributeRouteInfo { Constraints = GetConstraints(action.AttributeRouteInfo.Template, template), Defaults = GetDefaults(action, template), Optional = new List <string>(), Order = action.AttributeRouteInfo.Order, Precedence = RoutePrecedence.ComputeOutbound(template), }; _parser.Parse(template, info); return(info); }
/// <summary> /// Adds a new inbound route to the <see cref="TreeRouter"/>. /// </summary> /// <param name="handler">The <see cref="IRouter"/> for handling the route.</param> /// <param name="routeTemplate">The <see cref="RouteTemplate"/> of the route.</param> /// <param name="routeName">The route name.</param> /// <param name="order">The route order.</param> /// <returns>The <see cref="InboundRouteEntry"/>.</returns> public InboundRouteEntry MapInbound( IRouter handler, RouteTemplate routeTemplate, string routeName, int order) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } if (routeTemplate == null) { throw new ArgumentNullException(nameof(routeTemplate)); } var entry = new InboundRouteEntry() { Handler = handler, Order = order, Precedence = RoutePrecedence.ComputeInbound(routeTemplate), RouteName = routeName, RouteTemplate = routeTemplate, }; var constraintBuilder = new RouteConstraintBuilder(_constraintResolver, routeTemplate.TemplateText); foreach (var parameter in routeTemplate.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); } } } entry.Constraints = constraintBuilder.Build(); entry.Defaults = new RouteValueDictionary(); foreach (var parameter in entry.RouteTemplate.Parameters) { if (parameter.DefaultValue != null) { entry.Defaults.Add(parameter.Name, parameter.DefaultValue); } } InboundEntries.Add(entry); return(entry); }
public void AttributeRoute_GetEntries_CreatesInboundEntry_CombinesLikeActions() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{id}", Name = "BLOG_INDEX", Order = 17, }, RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { { "controller", "Blog" }, { "action", "Index" }, }, }, new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{id}", Name = "BLOG_INDEX", Order = 17, }, RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { { "controller", "Blog" }, { "action", "Index2" }, }, }, }; var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( builder.InboundEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); Assert.Empty(e.Defaults); }); }
private static decimal Compute(string template) { DefaultInlineConstraintResolver resolver = new DefaultInlineConstraintResolver(); HttpRouteValueDictionary defaults = new HttpRouteValueDictionary(); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); string standardRouteTemplate = InlineRouteTemplateParser.ParseRouteTemplate(template, defaults, constraints, new DefaultInlineConstraintResolver()); HttpParsedRoute parsedRoute = HttpRouteParser.Parse(standardRouteTemplate); return(RoutePrecedence.Compute(parsedRoute, constraints)); }
public override void AddEntry(string pattern, MatcherEndpoint endpoint) { var parsed = TemplateParser.Parse(pattern); _entries.Add(new Entry() { Order = 0, Pattern = parsed, Precedence = RoutePrecedence.ComputeInbound(parsed), Endpoint = endpoint, }); }
public void AttributeRoute_GetEntries_CreatesMatchingEntry_WithDefault() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{*slug=hello}", Name = "BLOG_INDEX", Order = 17, }, RouteConstraints = new List <RouteDataActionConstraint>() { new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), }, RouteValueDefaults = new Dictionary <string, object>() { { "controller", "Blog" }, { "action", "Index" }, }, }, }; var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var handler = CreateHandler().Object; var route = CreateRoute(handler, actionDescriptorProvider.Object); // Act var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( entries.MatchingEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{*slug=hello}", e.RouteTemplate.TemplateText); Assert.Same(handler, e.Target); Assert.Collection( e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair <string, object>(TreeRouter.RouteGroupKey, "1"), kvp), kvp => Assert.Equal(new KeyValuePair <string, object>("slug", "hello"), kvp)); Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); }
private static void Sort(List <RouteEntry> entries) { // We need to sort these in precedence order for the linear matchers. entries.Sort((x, y) => { var comparison = RoutePrecedence.ComputeInbound(x.Template).CompareTo(RoutePrecedence.ComputeInbound(y.Template)); if (comparison != 0) { return(comparison); } return(string.Compare(x.Template.TemplateText, y.Template.TemplateText, StringComparison.Ordinal)); }); }
public void AttributeRoute_GetEntries_CreatesInboundEntry_WithDefault() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{*slug=hello}", Name = "BLOG_INDEX", Order = 17, }, RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary <string, object>() { { "controller", "Blog" }, { "action", "Index" }, }, }, }; var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( builder.InboundEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{*slug=hello}", e.RouteTemplate.TemplateText); Assert.Collection( e.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair <string, object>(TreeRouter.RouteGroupKey, "1"), kvp), kvp => Assert.Equal(new KeyValuePair <string, object>("slug", "hello"), kvp)); }); }
public void AttributeRoute_GetEntries_CreatesLinkGenerationEntry_WithDefault() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{*slug=hello}", Name = "BLOG_INDEX", Order = 17, }, RouteConstraints = new List <RouteDataActionConstraint>() { new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), }, RouteValueDefaults = new Dictionary <string, object>() { { "controller", "Blog" }, { "action", "Index" }, }, }, }; var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( entries.LinkGenerationEntries, e => { Assert.NotNull(e.Binder); Assert.Empty(e.Constraints); Assert.Equal(new RouteValueDictionary(new { slug = "hello" }), e.Defaults); Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); Assert.Equal("BLOG_INDEX", e.Name); Assert.Equal(17, e.Order); Assert.Equal(actions[0].RouteValueDefaults, e.RequiredLinkValues); Assert.Equal("1", e.RouteGroup); Assert.Equal("api/Blog/{*slug=hello}", e.Template.TemplateText); }); }
public void AttributeRoute_GetEntries_CreatesOutboundEntry_WithConstraint() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{id:int}", Name = "BLOG_INDEX", Order = 17, }, RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { { TreeRouter.RouteGroupKey, "1" } }, RouteValueDefaults = new Dictionary <string, object>() { { "controller", "Blog" }, { "action", "Index" }, }, }, }; var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( builder.OutboundEntries, e => { Assert.Single(e.Constraints, kvp => kvp.Key == "id"); Assert.Empty(e.Defaults); Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); Assert.Equal(actions[0].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); Assert.Equal("api/Blog/{id:int}", e.RouteTemplate.TemplateText); }); }
/// <summary> /// Processes one attribute route /// </summary> /// <param name="action">Action to process</param> /// <returns>Route information</returns> private AttributeRouteInfo ProcessAttributeRoute(ControllerActionDescriptor action) { var template = TemplateParser.Parse(action.AttributeRouteInfo.Template); var info = new AttributeRouteInfo { Constraints = GetConstraints(action.AttributeRouteInfo.Template, template), Defaults = GetDefaults(action, template), Optional = new List <string>(), Order = action.AttributeRouteInfo.Order, Precedence = RoutePrecedence.ComputeOutbound(template), }; _parser.Parse(template, info); return(info); }
public int CompareTo(Entry other) { var comparison = Endpoint.Order.CompareTo(other.Endpoint.Order); if (comparison != 0) { return(comparison); } comparison = RoutePrecedence.ComputeInbound(Endpoint.ParsedTemplate).CompareTo(RoutePrecedence.ComputeInbound(other.Endpoint.ParsedTemplate)); if (comparison != 0) { return(comparison); } return(Endpoint.Template.CompareTo(other.Endpoint.Template)); }
private OutboundRouteEntry CreateOutboundRouteEntry(RouteEndpoint endpoint) { var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata <IRouteValuesAddressMetadata>(); var entry = new OutboundRouteEntry() { Handler = NullRouter.Instance, Order = endpoint.Order, Precedence = RoutePrecedence.ComputeOutbound(endpoint.RoutePattern), RequiredLinkValues = new RouteValueDictionary(routeValuesAddressMetadata?.RequiredValues), RouteTemplate = new RouteTemplate(endpoint.RoutePattern), Data = endpoint, RouteName = routeValuesAddressMetadata?.RouteName, }; entry.Defaults = new RouteValueDictionary(endpoint.RoutePattern.Defaults); return(entry); }
private InboundRouteEntry MapInbound(RouteTemplate template, Endpoint[] endpoints, int order) { if (template == null) { throw new ArgumentNullException(nameof(template)); } var entry = new InboundRouteEntry() { Precedence = RoutePrecedence.ComputeInbound(template), RouteTemplate = template, Order = order, Tag = endpoints, }; var constraintBuilder = new RouteConstraintBuilder(_constraintFactory, template.TemplateText); foreach (var parameter in template.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); } } } entry.Constraints = constraintBuilder.Build(); entry.Defaults = new RouteValueDictionary(); foreach (var parameter in entry.RouteTemplate.Parameters) { if (parameter.DefaultValue != null) { entry.Defaults.Add(parameter.Name, parameter.DefaultValue); } } return(entry); }
private OutboundRouteEntry CreateOutboundRouteEntry( RouteEndpoint endpoint, IReadOnlyDictionary <string, object> requiredValues, string routeName) { var entry = new OutboundRouteEntry() { Handler = NullRouter.Instance, Order = endpoint.Order, Precedence = RoutePrecedence.ComputeOutbound(endpoint.RoutePattern), RequiredLinkValues = new RouteValueDictionary(requiredValues), RouteTemplate = new RouteTemplate(endpoint.RoutePattern), Data = endpoint, RouteName = routeName, }; entry.Defaults = new RouteValueDictionary(endpoint.RoutePattern.Defaults); return(entry); }
internal RoutePattern( string rawText, IReadOnlyDictionary <string, object> defaults, IReadOnlyDictionary <string, IReadOnlyList <RoutePatternParameterPolicyReference> > parameterPolicies, IReadOnlyList <RoutePatternParameterPart> parameters, IReadOnlyList <RoutePatternPathSegment> pathSegments) { Debug.Assert(defaults != null); Debug.Assert(parameterPolicies != null); Debug.Assert(parameters != null); Debug.Assert(pathSegments != null); RawText = rawText; Defaults = defaults; ParameterPolicies = parameterPolicies; Parameters = parameters; PathSegments = pathSegments; InboundPrecedence = RoutePrecedence.ComputeInbound(this); OutboundPrecedence = RoutePrecedence.ComputeOutbound(this); }
internal RoutePattern( string rawText, Dictionary <string, object> defaults, Dictionary <string, IReadOnlyList <RoutePatternConstraintReference> > constraints, RoutePatternParameterPart[] parameters, RoutePatternPathSegment[] pathSegments) { Debug.Assert(defaults != null); Debug.Assert(constraints != null); Debug.Assert(parameters != null); Debug.Assert(pathSegments != null); RawText = rawText; Defaults = defaults; Constraints = constraints; Parameters = parameters; PathSegments = pathSegments; InboundPrecedence = RoutePrecedence.ComputeInbound(this); OutboundPrecedence = RoutePrecedence.ComputeOutbound(this); }
private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint) { var routeNameMetadata = endpoint.Metadata.GetMetadata <IRouteNameMetadata>(); var entry = new OutboundRouteEntry() { Handler = NullRouter.Instance, Order = endpoint.Order, Precedence = RoutePrecedence.ComputeOutbound(endpoint.ParsedTemplate), RequiredLinkValues = endpoint.RequiredValues, RouteTemplate = endpoint.ParsedTemplate, Data = endpoint, RouteName = routeNameMetadata?.Name, }; // TODO: review. These route constriants should be constructed when the endpoint // is built. This way they can be checked for validity on app startup too var constraintBuilder = new RouteConstraintBuilder( _inlineConstraintResolver, endpoint.ParsedTemplate.TemplateText); foreach (var parameter in endpoint.ParsedTemplate.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); } } } entry.Constraints = constraintBuilder.Build(); entry.Defaults = endpoint.Defaults; return(entry); }
private static RouteInfo GetRouteInfo( IInlineConstraintResolver constraintResolver, Dictionary <string, RouteTemplate> templateCache, ActionDescriptor action) { var constraint = action.RouteConstraints .FirstOrDefault(c => c.RouteKey == TreeRouter.RouteGroupKey); if (constraint == null || constraint.KeyHandling != RouteKeyHandling.RequireKey || constraint.RouteValue == null) { // This can happen if an ActionDescriptor has a route template, but doesn't have one of our // special route group constraints. This is a good indication that the user is using a 3rd party // routing system, or has customized their ADs in a way that we can no longer understand them. // // We just treat this case as an 'opt-out' of our attribute routing system. return(null); } var routeInfo = new RouteInfo() { ActionDescriptor = action, RouteGroup = constraint.RouteValue, RouteTemplate = action.AttributeRouteInfo.Template, }; try { RouteTemplate parsedTemplate; if (!templateCache.TryGetValue(action.AttributeRouteInfo.Template, out parsedTemplate)) { // Parsing with throw if the template is invalid. parsedTemplate = TemplateParser.Parse(action.AttributeRouteInfo.Template); templateCache.Add(action.AttributeRouteInfo.Template, parsedTemplate); } routeInfo.ParsedTemplate = parsedTemplate; } catch (Exception ex) { routeInfo.ErrorMessage = ex.Message; return(routeInfo); } foreach (var kvp in action.RouteValueDefaults) { foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (string.Equals(kvp.Key, parameter.Name, StringComparison.OrdinalIgnoreCase)) { routeInfo.ErrorMessage = Resources.FormatAttributeRoute_CannotContainParameter( routeInfo.RouteTemplate, kvp.Key, kvp.Value); return(routeInfo); } } } routeInfo.Order = action.AttributeRouteInfo.Order; routeInfo.MatchPrecedence = RoutePrecedence.ComputeMatched(routeInfo.ParsedTemplate); routeInfo.GenerationPrecedence = RoutePrecedence.ComputeGenerated(routeInfo.ParsedTemplate); routeInfo.Name = action.AttributeRouteInfo.Name; var constraintBuilder = new RouteConstraintBuilder(constraintResolver, routeInfo.RouteTemplate); foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var inlineConstraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, inlineConstraint.Constraint); } } } routeInfo.Constraints = constraintBuilder.Build(); routeInfo.Defaults = new RouteValueDictionary(); foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (parameter.DefaultValue != null) { routeInfo.Defaults.Add(parameter.Name, parameter.DefaultValue); } } return(routeInfo); }
private int InvokeCore() { if (!Input.HasValue() && !InputDirectory.HasValue()) { ShowHelp(); return(1); } if (Input.HasValue() && InputDirectory.HasValue()) { ShowHelp(); return(1); } if (!Output.HasValue()) { Output.Values.Add("Out.generated.cs"); } if (InputDirectory.HasValue()) { Input.Values.AddRange(Directory.EnumerateFiles(InputDirectory.Value(), "*.json", SearchOption.AllDirectories)); } Console.WriteLine($"Processing {Input.Values.Count} files..."); var entries = new List <RouteEntry>(); for (var i = 0; i < Input.Values.Count; i++) { var input = ReadInput(Input.Values[i]); ParseEntries(input, entries); } // We don't yet want to support complex segments. for (var i = entries.Count - 1; i >= 0; i--) { if (HasComplexSegment(entries[i])) { Out.WriteLine("Skipping route with complex segment: " + entries[i].Template.TemplateText); entries.RemoveAt(i); } } // The data that we're provided by might be unambiguous. // Remove any routes that would be ambiguous in our system. var routesByPrecedence = new Dictionary <decimal, List <RouteEntry> >(); for (var i = entries.Count - 1; i >= 0; i--) { var entry = entries[i]; var precedence = RoutePrecedence.ComputeInbound(entries[i].Template); if (!routesByPrecedence.TryGetValue(precedence, out var matches)) { matches = new List <RouteEntry>(); routesByPrecedence.Add(precedence, matches); } if (IsDuplicateTemplate(entry, matches)) { Out.WriteLine("Duplicate route template: " + entries[i].Template.TemplateText); entries.RemoveAt(i); continue; } matches.Add(entry); } // We're not too sophisticated with how we generate parameter values, just hoping for // the best. For parameters we generate a segment that is the same length as the parameter name // but with a minimum of 5 characters to avoid collisions. for (var i = entries.Count - 1; i >= 0; i--) { entries[i].RequestUrl = GenerateRequestUrl(entries[i].Template); if (entries[i].RequestUrl == null) { Out.WriteLine("Failed to create a request for: " + entries[i].Template.TemplateText); entries.RemoveAt(i); continue; } } Sort(entries); var text = Template.Execute(entries); File.WriteAllText(Output.Value(), text); return(0); }