Exemple #1
0
        public RequestDefinition(RouteRequestBuilder theWinningRoute, Shell shell)
        {
            Item         = theWinningRoute.Item;
            Section      = theWinningRoute.Section ?? Item?.CurrentItem;
            Content      = theWinningRoute.Content ?? Section?.CurrentItem;
            GlobalRoutes = theWinningRoute.GlobalRouteMatches;

            List <String> builder = new List <string>();

            if (Item?.Route != null)
            {
                builder.Add(Item.Route);
            }

            if (Section?.Route != null)
            {
                builder.Add(Section?.Route);
            }

            if (Content?.Route != null)
            {
                builder.Add(Content?.Route);
            }

            if (GlobalRoutes != null)
            {
                builder.AddRange(GlobalRoutes);
            }

            var uriPath = MakeUriString(builder);
            var uri     = ShellUriHandler.CreateUri(uriPath);

            FullUri = ShellUriHandler.ConvertToStandardFormat(shell, uri);
        }
Exemple #2
0
 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;
 }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
            }
        }
Exemple #5
0
        internal static List <RouteRequestBuilder> GetBestMatches(List <RouteRequestBuilder> possibleRoutePaths)
        {
            List <RouteRequestBuilder> bestMatches = new List <RouteRequestBuilder>();

            foreach (var match in possibleRoutePaths)
            {
                if (match.IsFullMatch)
                {
                    bool matchFound = false;

                    // remove duplicates
                    foreach (var bestMatch in bestMatches)
                    {
                        if (bestMatch.Item == match.Item &&
                            bestMatch.Section == match.Section &&
                            bestMatch.Content == match.Content &&
                            bestMatch.GlobalRouteMatches.Count == match.GlobalRouteMatches.Count)
                        {
                            bool allMatch = true;
                            for (var i = 0; i < bestMatch.GlobalRouteMatches.Count; i++)
                            {
                                if (bestMatch.GlobalRouteMatches[i] != match.GlobalRouteMatches[i])
                                {
                                    allMatch = false;
                                    break;
                                }
                            }

                            matchFound = allMatch;
                        }
                    }

                    if (!matchFound)
                    {
                        bestMatches.Add(match);
                    }
                }
            }

            while (bestMatches.Count > 1)
            {
                List <RouteRequestBuilder> betterMatches = new List <RouteRequestBuilder>();
                for (int i = bestMatches.Count - 1; i >= 0; i--)
                {
                    for (int j = i - 1; j >= 0; j--)
                    {
                        RouteRequestBuilder betterMatch = null;

                        if (bestMatches[j].MatchedParts > bestMatches[i].MatchedParts)
                        {
                            betterMatch = bestMatches[j];
                        }
                        else if (bestMatches[j].MatchedParts < bestMatches[i].MatchedParts)
                        {
                            betterMatch = bestMatches[i];
                        }

                        // nobody wins
                        if (betterMatch == null)
                        {
                            if (!betterMatches.Contains(bestMatches[i]))
                            {
                                betterMatches.Add(bestMatches[i]);
                            }

                            if (!betterMatches.Contains(bestMatches[j]))
                            {
                                betterMatches.Add(bestMatches[j]);
                            }
                        }
                        else if (betterMatch != null && !betterMatches.Contains(betterMatch))
                        {
                            betterMatches.Add(betterMatch);
                        }
                    }
                }

                // Nothing was trimmed on last pass
                if (bestMatches.Count == betterMatches.Count)
                {
                    return(betterMatches);
                }

                bestMatches = betterMatches;
            }

            return(bestMatches);
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        static List <RouteRequestBuilder> ProcessRelativeRoute(
            Shell shell,
            string[] routeKeys,
            string[] segments,
            bool enableRelativeShellRoutes,
            Uri originalRequest)
        {
            // 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)
                {
                    var matches = SearchForGlobalRoutes(segments, shell.CurrentState.FullLocation, currentLocation, routeKeys);
                    pureGlobalRoutesMatch.AddRange(matches);
                }

                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
                }
            }
            ;

            return(new List <RouteRequestBuilder>());
        }
Exemple #8
0
        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++)
            {
                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);

            var depthStart = 0;

            if (segments[0] == shell?.Route)
            {
                segments   = segments.Skip(1).ToArray();
                depthStart = 1;
            }
            else
            {
                depthStart = 0;
            }

            if (relativeMatch && shell?.CurrentItem != null)
            {
                var result = ProcessRelativeRoute(shell, routeKeys, segments, enableRelativeShellRoutes, originalRequest);
                if (result.Count > 0)
                {
                    return(result);
                }
            }

            possibleRoutePaths.Clear();
            SearchPath(shell, null, segments, possibleRoutePaths, depthStart);

            var bestMatches = GetBestMatches(possibleRoutePaths);

            if (bestMatches.Count > 0)
            {
                return(bestMatches);
            }

            bestMatches.Clear();
            ExpandOutGlobalRoutes(possibleRoutePaths, routeKeys);

            foreach (var possibleRoutePath in possibleRoutePaths)
            {
                if (possibleRoutePath.IsFullMatch)
                {
                    continue;
                }

                var url = possibleRoutePath.PathFull;

                var globalRouteMatches =
                    SearchForGlobalRoutes(
                        possibleRoutePath.RemainingSegments,
                        new ShellNavigationState(url, false).FullLocation,
                        possibleRoutePath.GetNodeLocation(),
                        routeKeys);

                if (globalRouteMatches.Count != 1)
                {
                    continue;
                }

                var globalRouteMatch = globalRouteMatches[0];

                while (possibleRoutePath.NextSegment != null)
                {
                    var matchIndex = globalRouteMatch.SegmentsMatched.IndexOf(possibleRoutePath.NextSegment);
                    if (matchIndex < 0)
                    {
                        break;
                    }

                    possibleRoutePath.AddGlobalRoute(
                        globalRouteMatch.GlobalRouteMatches[matchIndex],
                        globalRouteMatch.SegmentsMatched[matchIndex]);
                }
            }

            possibleRoutePaths = GetBestMatches(possibleRoutePaths);

            if (possibleRoutePaths.Count == 0)
            {
                foreach (var routeKey in routeKeys)
                {
                    if (routeKey == originalRequest.OriginalString)
                    {
                        var builder = new RouteRequestBuilder(routeKey, routeKey, null, new string[] { routeKey });
                        return(new List <RouteRequestBuilder> {
                            builder
                        });
                    }
                }

                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("//", "")}");
                        }
                    }
                }
            }
            return(possibleRoutePaths);
        }