/// <summary> /// Builds a <see cref="TreeRouter"/> with the <see cref="InboundEntries"/> /// and <see cref="OutboundEntries"/> defined in this <see cref="TreeRouteBuilder"/>. /// </summary> /// <param name="version">The version of the <see cref="TreeRouter"/>.</param> /// <returns>The <see cref="TreeRouter"/>.</returns> public TreeRouter Build(int version) { // Tree route builder builds a tree for each of the different route orders defined by // the user. When a route needs to be matched, the matching algorithm in tree router // just iterates over the trees in ascending order when it tries to match the route. var trees = new Dictionary <int, UrlMatchingTree>(); foreach (var entry in InboundEntries) { UrlMatchingTree tree; if (!trees.TryGetValue(entry.Order, out tree)) { tree = new UrlMatchingTree(entry.Order); trees.Add(entry.Order, tree); } AddEntryToTree(tree, entry); } return(new TreeRouter( trees.Values.OrderBy(tree => tree.Order).ToArray(), OutboundEntries, _urlEncoder, _objectPool, _logger, _constraintLogger, version)); }
public TreeRouter Build(int version) { var trees = new Dictionary <int, UrlMatchingTree>(); foreach (var entry in InboundEntries) { UrlMatchingTree tree; if (!trees.TryGetValue(entry.Order, out tree)) { tree = new UrlMatchingTree(entry.Order); trees.Add(entry.Order, tree); } AddEntryToTree(tree, entry); } return(new TreeRouter( trees.Values.OrderBy(tree => tree.Order).ToArray(), OutboundEntries, _urlEncoder, _objectPool, _logger, _constraintLogger, version)); }
public TreeRouter Build(int version) { var trees = new Dictionary <int, UrlMatchingTree>(); foreach (var entry in _matchingEntries) { UrlMatchingTree tree; if (!trees.TryGetValue(entry.Order, out tree)) { tree = new UrlMatchingTree(entry.Order); trees.Add(entry.Order, tree); } AddEntryToTree(tree, entry); } return(new TreeRouter( _target, trees.Values.OrderBy(tree => tree.Order).ToArray(), _generatingEntries, _logger, _constraintLogger, version)); }
private void AddEntryToTree(UrlMatchingTree tree, InboundRouteEntry entry) { // The url matching tree represents all the routes asociated with a given // order. Each node in the tree represents all the different categories // a segment can have for which there is a defined inbound route entry. // Each node contains a set of Matches that indicate all the routes for which // a URL is a potential match. This list contains the routes with the same // number of segments and the routes with the same number of segments plus an // additional catch all parameter (as it can be empty). // For example, for a set of routes like: // 'Customer/Index/{id}' // '{Controller}/{Action}/{*parameters}' // // The route tree will look like: // Root -> // Literals: Customer -> // Literals: Index -> // Parameters: {id} // Matches: 'Customer/Index/{id}' // Parameters: {Controller} -> // Parameters: {Action} -> // Matches: '{Controller}/{Action}/{*parameters}' // CatchAlls: {*parameters} // Matches: '{Controller}/{Action}/{*parameters}' // // When the tree router tries to match a route, it iterates the list of url matching trees // in ascending order. For each tree it traverses each node starting from the root in the // following order: Literals, constrained parameters, parameters, constrained catch all routes, catch alls. // When it gets to a node of the same length as the route its trying to match, it simply looks at the list of // candidates (which is in precence order) and tries to match the url against it. // var current = tree.Root; var matcher = new TemplateMatcher(entry.RouteTemplate, entry.Defaults); for (var i = 0; i < entry.RouteTemplate.Segments.Count; i++) { var segment = entry.RouteTemplate.Segments[i]; if (!segment.IsSimple) { // Treat complex segments as a constrained parameter if (current.ConstrainedParameters == null) { current.ConstrainedParameters = new UrlMatchingNode(length: i + 1); } current = current.ConstrainedParameters; continue; } Debug.Assert(segment.Parts.Count == 1); var part = segment.Parts[0]; if (part.IsLiteral) { UrlMatchingNode next; if (!current.Literals.TryGetValue(part.Text, out next)) { next = new UrlMatchingNode(length: i + 1); current.Literals.Add(part.Text, next); } current = next; continue; } if (part.IsParameter && (part.IsOptional || part.IsCatchAll || part.DefaultValue != null)) { current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher }); } if (part.IsParameter && part.InlineConstraints.Any() && !part.IsCatchAll) { if (current.ConstrainedParameters == null) { current.ConstrainedParameters = new UrlMatchingNode(length: i + 1); } current = current.ConstrainedParameters; continue; } if (part.IsParameter && !part.IsCatchAll) { if (current.Parameters == null) { current.Parameters = new UrlMatchingNode(length: i + 1); } current = current.Parameters; continue; } if (part.IsParameter && part.InlineConstraints.Any() && part.IsCatchAll) { if (current.ConstrainedCatchAlls == null) { current.ConstrainedCatchAlls = new UrlMatchingNode(length: i + 1) { IsCatchAll = true }; } current = current.ConstrainedCatchAlls; continue; } if (part.IsParameter && part.IsCatchAll) { if (current.CatchAlls == null) { current.CatchAlls = new UrlMatchingNode(length: i + 1) { IsCatchAll = true }; } current = current.CatchAlls; continue; } Debug.Fail("We shouldn't get here."); } current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher }); current.Matches.Sort((x, y) => { var result = x.Entry.Precedence.CompareTo(y.Entry.Precedence); return(result == 0 ? x.Entry.RouteTemplate.TemplateText.CompareTo(y.Entry.RouteTemplate.TemplateText) : result); }); }
private void AddEntryToTree(UrlMatchingTree tree, InboundRouteEntry entry) { var current = tree.Root; var matcher = new TemplateMatcher(entry.RouteTemplate, entry.Defaults); for (var i = 0; i < entry.RouteTemplate.Segments.Count; i++) { var segment = entry.RouteTemplate.Segments[i]; if (!segment.IsSimple) { // Treat complex segments as a constrained parameter if (current.ConstrainedParameters == null) { current.ConstrainedParameters = new UrlMatchingNode(length: i + 1); } current = current.ConstrainedParameters; continue; } Debug.Assert(segment.Parts.Count == 1); var part = segment.Parts[0]; if (part.IsLiteral) { UrlMatchingNode next; if (!current.Literals.TryGetValue(part.Text, out next)) { next = new UrlMatchingNode(length: i + 1); current.Literals.Add(part.Text, next); } current = next; continue; } if (part.IsParameter && (part.IsOptional || part.IsCatchAll)) { current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher }); } if (part.IsParameter && part.InlineConstraints.Any() && !part.IsCatchAll) { if (current.ConstrainedParameters == null) { current.ConstrainedParameters = new UrlMatchingNode(length: i + 1); } current = current.ConstrainedParameters; continue; } if (part.IsParameter && !part.IsCatchAll) { if (current.Parameters == null) { current.Parameters = new UrlMatchingNode(length: i + 1); } current = current.Parameters; continue; } if (part.IsParameter && part.InlineConstraints.Any() && part.IsCatchAll) { if (current.ConstrainedCatchAlls == null) { current.ConstrainedCatchAlls = new UrlMatchingNode(length: i + 1) { IsCatchAll = true }; } current = current.ConstrainedCatchAlls; continue; } if (part.IsParameter && part.IsCatchAll) { if (current.CatchAlls == null) { current.CatchAlls = new UrlMatchingNode(length: i + 1) { IsCatchAll = true }; } current = current.CatchAlls; continue; } Debug.Fail("We shouldn't get here."); } current.Matches.Add(new InboundMatch() { Entry = entry, TemplateMatcher = matcher }); current.Matches.Sort((x, y) => { var result = x.Entry.Precedence.CompareTo(y.Entry.Precedence); return(result == 0 ? x.Entry.RouteTemplate.TemplateText.CompareTo(y.Entry.RouteTemplate.TemplateText) : result); }); }