Exemple #1
0
        public void AddPolicyEdge(object state, DfaNode node)
        {
            if (PolicyEdges == null)
            {
                PolicyEdges = new Dictionary <object, DfaNode>();
            }

            PolicyEdges.Add(state, node);
        }
Exemple #2
0
        public void AddLiteral(string literal, DfaNode node)
        {
            if (Literals == null)
            {
                Literals = new Dictionary <string, DfaNode>(StringComparer.OrdinalIgnoreCase);
            }

            Literals.Add(literal, node);
        }
        public DfaNode BuildDfaTree()
        {
            // We build the tree by doing a BFS over the list of entries. This is important
            // because a 'parameter' node can also traverse the same paths that literal nodes
            // traverse. This means that we need to order the entries first, or else we will
            // miss possible edges in the DFA.
            _endpoints.Sort(_comparer);

            // Since we're doing a BFS we will process each 'level' of the tree in stages
            // this list will hold the set of items we need to process at the current
            // stage.
            var work = new List <(MatcherEndpoint endpoint, List <DfaNode> parents)>();

            var root = new DfaNode()
            {
                Depth = 0, Label = "/"
            };

            // To prepare for this we need to compute the max depth, as well as
            // a seed list of items to process (entry, root).
            var maxDepth = 0;

            for (var i = 0; i < _endpoints.Count; i++)
            {
                var endpoint = _endpoints[i];
                maxDepth = Math.Max(maxDepth, endpoint.RoutePattern.PathSegments.Count);

                work.Add((endpoint, new List <DfaNode>()
                {
                    root,
                }));
            }

            // Now we process the entries a level at a time.
            for (var depth = 0; depth <= maxDepth; depth++)
            {
                // As we process items, collect the next set of items.
                var nextWork = new List <(MatcherEndpoint endpoint, List <DfaNode> parents)>();

                for (var i = 0; i < work.Count; i++)
                {
                    var(endpoint, parents) = work[i];

                    if (!HasAdditionalRequiredSegments(endpoint, depth))
                    {
                        for (var j = 0; j < parents.Count; j++)
                        {
                            var parent = parents[j];
                            parent.Matches.Add(endpoint);
                        }
                    }

                    // Find the parents of this edge at the current depth
                    var nextParents = new List <DfaNode>();
                    var segment     = GetCurrentSegment(endpoint, depth);
                    if (segment == null)
                    {
                        continue;
                    }

                    for (var j = 0; j < parents.Count; j++)
                    {
                        var parent = parents[j];
                        var part   = segment.Parts[0];
                        if (segment.IsSimple && part is RoutePatternLiteralPart literalPart)
                        {
                            var literal = literalPart.Content;
                            if (!parent.Literals.TryGetValue(literal, out var next))
                            {
                                next = new DfaNode()
                                {
                                    Depth = parent.Depth + 1,
                                    Label = parent.Label + literal + "/",
                                };
                                parent.Literals.Add(literal, next);
                            }

                            nextParents.Add(next);
                        }
                        else if (segment.IsSimple && part is RoutePatternParameterPart parameterPart && parameterPart.IsCatchAll)
                        {
                            // A catch all should traverse all literal nodes as well as parameter nodes
                            // we don't need to create the parameter node here because of ordering
                            // all catchalls will be processed after all parameters.
                            nextParents.AddRange(parent.Literals.Values);
                            if (parent.Parameters != null)
                            {
                                nextParents.Add(parent.Parameters);
                            }

                            // We also create a 'catchall' here. We don't do further traversals
                            // on the catchall node because only catchalls can end up here. The
                            // catchall node allows us to capture an unlimited amount of segments
                            // and also to match a zero-length segment, which a parameter node
                            // doesn't allow.
                            if (parent.CatchAll == null)
                            {
                                parent.CatchAll = new DfaNode()
                                {
                                    Depth = parent.Depth + 1,
                                    Label = parent.Label + "{*...}/",
                                };

                                // The catchall node just loops.
                                parent.CatchAll.Parameters = parent.CatchAll;
                                parent.CatchAll.CatchAll   = parent.CatchAll;
                            }

                            parent.CatchAll.Matches.Add(endpoint);
                        }