static List <RouteRequestBuilder> SearchForGlobalRoutes( string[] segments, Uri startingFrom, NodeLocation currentLocation, string[] routeKeys) { List <RouteRequestBuilder> pureGlobalRoutesMatch = new List <RouteRequestBuilder>(); string newPath = String.Join(_pathSeparator, segments); var currentSegments = RetrievePaths(startingFrom.OriginalString); var newSegments = CollapsePath(newPath, currentSegments, true).ToArray(); List <string> fullRouteWithNewSegments = new List <string>(currentSegments); fullRouteWithNewSegments.AddRange(newSegments); // This is used to calculate if the global route matches RouteRequestBuilder routeRequestBuilder = new RouteRequestBuilder(fullRouteWithNewSegments.ToArray()); // add shell element routes routeRequestBuilder.AddMatch(currentLocation); // add routes that are contributed by global routes for (var i = 0; i < currentSegments.Length; i++) { var currentSeg = currentSegments[i]; if (routeRequestBuilder.FullSegments.Count <= i || currentSeg != routeRequestBuilder.FullSegments[i]) { routeRequestBuilder.AddGlobalRoute(currentSeg, currentSeg); } } var existingGlobalRoutes = routeRequestBuilder.GlobalRouteMatches.ToList(); ExpandOutGlobalRoutes(new List <RouteRequestBuilder> { routeRequestBuilder }, routeKeys); if (routeRequestBuilder.IsFullMatch) { RouteRequestBuilder requestBuilderWithNewSegments = new RouteRequestBuilder(newSegments); var additionalRouteMatches = routeRequestBuilder.GlobalRouteMatches; for (int i = existingGlobalRoutes.Count; i < additionalRouteMatches.Count; i++) { requestBuilderWithNewSegments.AddGlobalRoute(additionalRouteMatches[i], segments[i - existingGlobalRoutes.Count]); } pureGlobalRoutesMatch.Add(requestBuilderWithNewSegments); } return(pureGlobalRoutesMatch); }
static void SearchPath( object node, RouteRequestBuilder currentMatchedPath, string[] segments, List <RouteRequestBuilder> possibleRoutePaths, int depthToStart, int myDepth = -1, NodeLocation currentLocation = null, bool ignoreGlobalRoutes = true) { if (node is GlobalRouteItem && ignoreGlobalRoutes) { return; } ++myDepth; currentLocation = currentLocation ?? new NodeLocation(); currentLocation.SetNode(node); IEnumerable items = null; if (depthToStart > myDepth) { items = GetItems(node); if (items == null) { return; } foreach (var nextNode in items) { SearchPath(nextNode, null, segments, possibleRoutePaths, depthToStart, myDepth, currentLocation, ignoreGlobalRoutes); } return; } string shellSegment = GetRoute(node); string userSegment = null; if (currentMatchedPath == null) { userSegment = segments[0]; } else { userSegment = currentMatchedPath.NextSegment; } if (userSegment == null) { return; } RouteRequestBuilder builder = null; if (shellSegment == userSegment || Routing.IsImplicit(shellSegment)) { if (currentMatchedPath == null) { builder = new RouteRequestBuilder(shellSegment, userSegment, node, segments); } else { builder = new RouteRequestBuilder(currentMatchedPath); builder.AddMatch(shellSegment, userSegment, node); } if (!Routing.IsImplicit(shellSegment) || shellSegment == userSegment) { possibleRoutePaths.Add(builder); } } items = GetItems(node); if (items == null) { return; } foreach (var nextNode in items) { SearchPath(nextNode, builder, segments, possibleRoutePaths, depthToStart, myDepth, currentLocation, ignoreGlobalRoutes); } }
static bool FindAndAddSegmentMatch(RouteRequestBuilder possibleRoutePath, string[] routeKeys) { // First search by collapsing global routes if user is registering routes like "route1/route2/route3" foreach (var routeKey in routeKeys) { var collapsedRoutes = CollapsePath(routeKey, possibleRoutePath.SegmentsMatched, true); var collapsedRoute = String.Join(_pathSeparator, collapsedRoutes); if (routeKey.StartsWith("//")) { var routeKeyPaths = routeKey.Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries); if (routeKeyPaths[0] == collapsedRoutes[0]) { collapsedRoute = "//" + collapsedRoute; } } string collapsedMatch = possibleRoutePath.GetNextSegmentMatch(collapsedRoute); if (!String.IsNullOrWhiteSpace(collapsedMatch)) { possibleRoutePath.AddGlobalRoute(routeKey, collapsedMatch); return(true); } // If the registered route is a combination of shell items and global routes then we might end up here // without the previous tree search finding the correct path if ((possibleRoutePath.Shell != null) && (possibleRoutePath.Item == null || possibleRoutePath.Section == null || possibleRoutePath.Content == null)) { var nextNode = possibleRoutePath.GetNodeLocation().WalkToNextNode(); while (nextNode != null) { // This means we've jumped to a branch that no longer corresponds with the route path we are searching if ((possibleRoutePath.Item != null && nextNode.Item != possibleRoutePath.Item) || (possibleRoutePath.Section != null && nextNode.Section != possibleRoutePath.Section) || (possibleRoutePath.Content != null && nextNode.Content != possibleRoutePath.Content)) { nextNode = nextNode.WalkToNextNode(); continue; } var leafSearch = new RouteRequestBuilder(possibleRoutePath); if (!leafSearch.AddMatch(nextNode)) { nextNode = nextNode.WalkToNextNode(); continue; } var collapsedLeafRoute = String.Join(_pathSeparator, CollapsePath(routeKey, leafSearch.SegmentsMatched, true)); if (routeKey.StartsWith("//")) { collapsedLeafRoute = "//" + collapsedLeafRoute; } string segmentMatch = leafSearch.GetNextSegmentMatch(collapsedLeafRoute); if (!String.IsNullOrWhiteSpace(segmentMatch)) { possibleRoutePath.AddMatch(nextNode); possibleRoutePath.AddGlobalRoute(routeKey, segmentMatch); return(true); } nextNode = nextNode.WalkToNextNode(); } } } // check for exact matches if (routeKeys.Contains(possibleRoutePath.NextSegment)) { possibleRoutePath.AddGlobalRoute(possibleRoutePath.NextSegment, possibleRoutePath.NextSegment); return(true); } return(false); }
internal static List <RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request, Uri originalRequest, bool enableRelativeShellRoutes) { var routeKeys = Routing.GetRouteKeys(); for (int i = 0; i < routeKeys.Length; i++) { if (routeKeys[i] == originalRequest.OriginalString) { var builder = new RouteRequestBuilder(routeKeys[i], routeKeys[i], null, new string[] { routeKeys[i] }); return(new List <RouteRequestBuilder> { builder }); } routeKeys[i] = FormatUri(routeKeys[i]); } request = FormatUri(request, shell); originalRequest = FormatUri(originalRequest, shell); List <RouteRequestBuilder> possibleRoutePaths = new List <RouteRequestBuilder>(); if (!request.IsAbsoluteUri) { request = ConvertToStandardFormat(shell, request); } string localPath = request.LocalPath; bool relativeMatch = false; if (!originalRequest.IsAbsoluteUri && !originalRequest.OriginalString.StartsWith("//", StringComparison.Ordinal)) { relativeMatch = true; } var segments = RetrievePaths(localPath); if (!relativeMatch) { for (int i = 0; i < routeKeys.Length; i++) { var route = routeKeys[i]; var uri = ConvertToStandardFormat(shell, CreateUri(route)); if (uri.Equals(request)) { throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {originalRequest.OriginalString.Replace("//", "")}"); //var builder = new RouteRequestBuilder(route, route, null, segments); //return new List<RouteRequestBuilder> { builder }; } } } var depthStart = 0; if (segments[0] == shell?.Route) { segments = segments.Skip(1).ToArray(); depthStart = 1; } else { depthStart = 0; } if (relativeMatch && shell?.CurrentItem != null) { // retrieve current location var currentLocation = NodeLocation.Create(shell); while (currentLocation.Shell != null) { var pureRoutesMatch = new List <RouteRequestBuilder>(); var pureGlobalRoutesMatch = new List <RouteRequestBuilder>(); //currently relative routes to shell routes isn't supported as we aren't creating navigation stacks if (enableRelativeShellRoutes) { SearchPath(currentLocation.LowestChild, null, segments, pureRoutesMatch, 0); ExpandOutGlobalRoutes(pureRoutesMatch, routeKeys); pureRoutesMatch = GetBestMatches(pureRoutesMatch); if (pureRoutesMatch.Count > 0) { return(pureRoutesMatch); } } SearchPath(currentLocation.LowestChild, null, segments, pureGlobalRoutesMatch, 0, ignoreGlobalRoutes: false); ExpandOutGlobalRoutes(pureGlobalRoutesMatch, routeKeys); if (currentLocation.Content != null && pureGlobalRoutesMatch.Count == 0) { string newPath = $"{shell.CurrentState.Location.OriginalString}{_pathSeparator}{String.Join(_pathSeparator, segments)}"; var currentSegments = RetrievePaths(shell.CurrentState.FullLocation.OriginalString); var newSegments = RetrievePaths(newPath); RouteRequestBuilder routeRequestBuilder = new RouteRequestBuilder(newSegments); RouteRequestBuilder requestBuilderWithNewSegments = new RouteRequestBuilder(segments); // add shell element routes routeRequestBuilder.AddMatch(currentLocation); // add routes that are contributed by global routes for (var i = 0; i < currentSegments.Length; i++) { var currentSeg = currentSegments[i]; if (routeRequestBuilder.FullSegments.Count <= i || currentSeg != routeRequestBuilder.FullSegments[i]) { routeRequestBuilder.AddGlobalRoute(currentSeg, currentSeg); } } var existingGlobalRoutes = routeRequestBuilder.GlobalRouteMatches.ToList(); ExpandOutGlobalRoutes(new List <RouteRequestBuilder> { routeRequestBuilder }, routeKeys); if (routeRequestBuilder.IsFullMatch) { var additionalRouteMatches = routeRequestBuilder.GlobalRouteMatches; for (int i = existingGlobalRoutes.Count; i < additionalRouteMatches.Count; i++) { requestBuilderWithNewSegments.AddGlobalRoute(additionalRouteMatches[i], segments[i - existingGlobalRoutes.Count]); } pureGlobalRoutesMatch.Add(requestBuilderWithNewSegments); } } pureGlobalRoutesMatch = GetBestMatches(pureGlobalRoutesMatch); if (pureGlobalRoutesMatch.Count > 0) { // currently relative routes to shell routes isn't supported as we aren't creating navigation stacks // So right now we will just throw an exception so that once this is implemented // GotoAsync doesn't start acting inconsistently and all of a sudden starts creating routes int shellElementsMatched = pureGlobalRoutesMatch[0].SegmentsMatched.Count - pureGlobalRoutesMatch[0].GlobalRouteMatches.Count; if (!enableRelativeShellRoutes && shellElementsMatched > 0) { throw new Exception($"Relative routing to shell elements is currently not supported. Try prefixing your uri with ///: ///{originalRequest}"); } return(pureGlobalRoutesMatch); } currentLocation.Pop(); } string searchPath = String.Join(_pathSeparator, segments); if (routeKeys.Contains(searchPath)) { return(new List <RouteRequestBuilder> { new RouteRequestBuilder(searchPath, searchPath, null, segments) }); } RouteRequestBuilder builder = null; foreach (var segment in segments) { if (routeKeys.Contains(segment)) { if (builder == null) { builder = new RouteRequestBuilder(segment, segment, null, segments); } else { builder.AddGlobalRoute(segment, segment); } } } if (builder != null && builder.IsFullMatch) { return new List <RouteRequestBuilder> { builder } } ; } else { possibleRoutePaths.Clear(); SearchPath(shell, null, segments, possibleRoutePaths, depthStart); var bestMatches = GetBestMatches(possibleRoutePaths); if (bestMatches.Count > 0) { return(bestMatches); } bestMatches.Clear(); ExpandOutGlobalRoutes(possibleRoutePaths, routeKeys); } possibleRoutePaths = GetBestMatches(possibleRoutePaths); return(possibleRoutePaths); }