| File: Shell\RouteRequestBuilder.cs | Web Access |
| Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls) |
#nullable disable using System; using System.Collections.Generic; using System.Linq; namespace Microsoft.Maui.Controls { /// <summary> /// This attempts to locate the intended route trying to be navigated to /// </summary> internal class RouteRequestBuilder { readonly List<string> _globalRouteMatches = new List<string>(); readonly List<string> _matchedSegments = new List<string>(); readonly List<string> _fullSegments = new List<string>(); readonly List<string> _allSegments = null; readonly static string _uriSeparator = "/"; public Shell Shell { get; private set; } public ShellItem Item { get; private set; } public ShellSection Section { get; private set; } public ShellContent Content { get; private set; } public object LowestChild => (object)Content ?? (object)Section ?? (object)Item ?? (object)Shell; public RouteRequestBuilder(List<string> allSegments) { _allSegments = allSegments; } public RouteRequestBuilder(string shellSegment, string userSegment, object node, List<string> allSegments) : this(allSegments) { if (node != null) AddMatch(shellSegment, userSegment, node); else AddGlobalRoute(userSegment, shellSegment); } public RouteRequestBuilder(RouteRequestBuilder builder) : this(builder._allSegments) { _matchedSegments.AddRange(builder._matchedSegments); _fullSegments.AddRange(builder._fullSegments); _globalRouteMatches.AddRange(builder._globalRouteMatches); Shell = builder.Shell; Item = builder.Item; Section = builder.Section; Content = builder.Content; } public void AddGlobalRoute(string routeName, string segment) { _globalRouteMatches.Add(routeName); foreach (string path in ShellUriHandler.RetrievePaths(segment)) { _fullSegments.Add(path); _matchedSegments.Add(path); } } public bool AddMatch(ShellUriHandler.NodeLocation nodeLocation) { if (Item == null && !AddNode(nodeLocation.Item, NextSegment)) return false; if (Section == null && !AddNode(nodeLocation.Section, NextSegment)) return false; if (Content == null && !AddNode(nodeLocation.Content, NextSegment)) return false; return true; bool AddNode(BaseShellItem baseShellItem, string nextSegment) { _ = baseShellItem ?? throw new ArgumentNullException(nameof(baseShellItem)); if (Routing.IsUserDefined(baseShellItem.Route) && baseShellItem.Route != nextSegment) { return false; } AddMatch(baseShellItem.Route, GetUserSegment(baseShellItem), baseShellItem); return true; } string GetUserSegment(BaseShellItem baseShellItem) { if (Routing.IsUserDefined(baseShellItem)) return baseShellItem.Route; return String.Empty; } } public void AddMatch(string shellSegment, string userSegment, object node) { if (node == null) throw new ArgumentNullException(nameof(node)); switch (node) { case ShellUriHandler.GlobalRouteItem globalRoute: if (globalRoute.IsFinished) _globalRouteMatches.Add(globalRoute.SourceRoute); break; case Shell shell: if (shell == Shell) return; Shell = shell; break; case ShellItem item: if (Item == item) return; Item = item; break; case ShellSection section: if (Section == section) return; Section = section; if (Item == null) { Item = Section.Parent as ShellItem; _fullSegments.Add(Item.Route); } break; case ShellContent content: if (Content == content) return; Content = content; if (Section == null) { Section = Content.Parent as ShellSection; _fullSegments.Add(Section.Route); } if (Item == null) { Item = Section.Parent as ShellItem; _fullSegments.Insert(0, Item.Route); } break; } if (Item?.Parent is Shell s) Shell = s; // if shellSegment == userSegment it means the implicit route is part of the request if (Routing.IsUserDefined(shellSegment) || shellSegment == userSegment || shellSegment == NextSegment) _matchedSegments.Add(shellSegment); _fullSegments.Add(shellSegment); } public string GetNextSegmentMatch(string matchMe) { var segmentsToMatch = ShellUriHandler.RetrievePaths(matchMe).ToList(); // if matchMe is an absolute route then we only match // if there are no routes already present if (matchMe.StartsWith("/", StringComparison.Ordinal) || matchMe.StartsWith("\\", StringComparison.Ordinal)) { for (var i = 0; i < _matchedSegments.Count; i++) { var seg = _matchedSegments[i]; if (segmentsToMatch.Count <= i || segmentsToMatch[i] != seg) return String.Empty; segmentsToMatch.Remove(seg); } } List<string> matches = new List<string>(); List<string> currentSet = new List<string>(_matchedSegments); foreach (var split in segmentsToMatch) { string next = GetNextSegment(currentSet); if (next == split) { currentSet.Add(split); matches.Add(split); } else { return String.Empty; } } return String.Join(_uriSeparator, matches); } string GetNextSegment(IReadOnlyList<string> matchedSegments) { var nextMatch = matchedSegments.Count; if (nextMatch >= _allSegments.Count) return null; return _allSegments[nextMatch]; } public string NextSegment { get => GetNextSegment(_matchedSegments); } public string RemainingPath { get { var nextMatch = _matchedSegments.Count; if (nextMatch >= _allSegments.Count) return null; return Routing.FormatRoute(String.Join(_uriSeparator, _allSegments.Skip(nextMatch))); } } public List<string> RemainingSegments { get { var nextMatch = _matchedSegments.Count; if (nextMatch >= _allSegments.Count) return null; return _allSegments.Skip(nextMatch).ToList(); } } string MakeUriString(List<string> segments) { if (segments[0].StartsWith(_uriSeparator, StringComparison.Ordinal) || segments[0].StartsWith("\\", StringComparison.Ordinal)) return String.Join(_uriSeparator, segments); return $"//{String.Join(_uriSeparator, segments)}"; } public int MatchedParts { get { int count = GlobalRouteMatches.Count; if (Item != null) count++; if (Content != null) count++; if (Section != null) count++; return count; } } public string PathNoImplicit => MakeUriString(_matchedSegments); public string PathFull => MakeUriString(_fullSegments); public bool IsFullMatch => _matchedSegments.Count == _allSegments.Count; public List<string> GlobalRouteMatches => _globalRouteMatches; public List<string> SegmentsMatched => _matchedSegments; public IReadOnlyList<string> FullSegments => _fullSegments; public ShellUriHandler.NodeLocation GetNodeLocation() { ShellUriHandler.NodeLocation nodeLocation = new ShellUriHandler.NodeLocation(); nodeLocation.SetNode(Item); nodeLocation.SetNode(Section); nodeLocation.SetNode(Content); return nodeLocation; } } }