public ConventionalRoutingActionDescriptorCollectionProvider(
            IEnumerable <IActionDescriptorProvider> actionDescriptorProviders,
            IEnumerable <IActionDescriptorChangeProvider> actionDescriptorChangeProviders,
            IRouteTemplateResolver routeTemplateResolver,
            IOptions <SwaggerRoutingOptions> swaggerRoutingOptions)
        {
            _routeTemplateResolver     = routeTemplateResolver;
            _actionDescriptorProviders = actionDescriptorProviders
                                         .OrderBy(p => p.Order)
                                         .ToArray();

            _actionDescriptorChangeProviders = actionDescriptorChangeProviders.ToArray();

            _lock = new object();

            // IMPORTANT: this needs to be the last thing we do in the constructor. Change notifications can happen immediately!
            ChangeToken.OnChange(
                GetCompositeChangeToken,
                UpdateCollection);

            if (swaggerRoutingOptions != null)
            {
                _swaggerRoutingOptions = swaggerRoutingOptions.Value;
            }
        }
        public string ResolveRouteTemplate(ActionDescriptor actionDescriptor, SwaggerRoutingOptions options)
        {
            var routes           = ConventionalRoutingSwaggerGen.ROUTES;
            var template         = string.Empty;
            var templateDefaults = new Dictionary <string, string>();

            if (routes != null)
            {
                foreach (var router in routes)
                {
                    templateDefaults.Clear();
                    var isDefaultArea       = false;
                    var isDefaultController = false;
                    var isDefaultAction     = false;

                    var route = router as Route;
                    if (route != null)
                    {
                        if (options?.IgnoreTemplateFunc != null)
                        {
                            var ignoreRoute = options.IgnoreTemplateFunc(route.RouteTemplate);
                            if (ignoreRoute)
                            {
                                continue;
                            }
                        }

                        var routeArea       = GetRouteArea(route, out bool isAreaParameter);
                        var routeController = GetRouteController(route, out var isControllerParameter);
                        var routeAction     = GetRouteAction(route, out bool isActionParameter);

                        var actionDescArea       = GetActionDescriptorArea(actionDescriptor);
                        var actionDescController = GetActionDescriptorController(actionDescriptor);
                        var actionDescAction     = GetActionDescriptorAction(actionDescriptor);

                        var routeMatchConfig = new MatchConfig(routeArea, routeController, routeAction)
                        {
                            IsAreaParameter       = isAreaParameter,
                            IsControllerParameter = isControllerParameter,
                            IsActionParameter     = isActionParameter
                        };

                        var actionMatchConfig = new MatchConfig(actionDescArea, actionDescController, actionDescAction);

                        if (MatchConfig.Match(routeMatchConfig, actionMatchConfig))
                        {
                            var paramIndex           = 0;
                            var actionParametersUsed = new List <string>();
                            foreach (var segment in route.ParsedTemplate.Segments)
                            {
                                var firstPart = segment.Parts.First();

                                var hasConstraint = route.Constraints != null &&
                                                    route.Constraints.Any(c => c.Key.Equals(firstPart.Name));

                                IRouteConstraint routeConstraint = null;
                                bool             passConstraint  = false;
                                if (hasConstraint)
                                {
                                    routeConstraint = route.Constraints[firstPart.Name];
                                }

                                if (firstPart.IsLiteral)
                                {
                                    template += $"{firstPart.Text}/";
                                }
                                else if (firstPart.Name.Equals("area"))
                                {
                                    if (string.IsNullOrEmpty(actionDescArea))
                                    {
                                        template = null;
                                        break;
                                    }

                                    if (hasConstraint &&
                                        (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint)))
                                    {
                                        passConstraint =
                                            PassConstraint(actionMatchConfig.Area, routeConstraint);

                                        if (!passConstraint)
                                        {
                                            template = null;
                                            break;
                                        }
                                    }

                                    if (!string.IsNullOrEmpty(actionMatchConfig.Area) && route.Defaults != null &&
                                        route.Defaults.TryGetValue("area", out var defaultArea) &&
                                        !string.IsNullOrEmpty(defaultArea?.ToString()))
                                    {
                                        isDefaultArea = actionMatchConfig.Area
                                                        .Equals(defaultArea.ToString(),
                                                                StringComparison.InvariantCultureIgnoreCase);
                                        if (isDefaultArea)
                                        {
                                            templateDefaults.Add("area", actionMatchConfig.Area);
                                        }
                                    }

                                    template += $"{actionMatchConfig.Area}/";
                                }
                                else if (firstPart.Name.Equals("controller"))
                                {
                                    if (hasConstraint &&
                                        (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint)))
                                    {
                                        passConstraint =
                                            PassConstraint(actionMatchConfig.Controller, routeConstraint);

                                        if (!passConstraint)
                                        {
                                            template = null;
                                            break;
                                        }
                                    }

                                    if (route.Defaults != null &&
                                        route.Defaults.TryGetValue("controller", out var defaultController) &&
                                        !string.IsNullOrEmpty(defaultController?.ToString()))
                                    {
                                        var configController = WithNoSuffix(actionMatchConfig.Controller, "Controller")
                                                               .ToLower();
                                        isDefaultController = configController.Equals(defaultController.ToString(),
                                                                                      StringComparison.InvariantCultureIgnoreCase);

                                        if (isDefaultController)
                                        {
                                            templateDefaults.Add("controller",
                                                                 $"{WithNoSuffix(actionMatchConfig.Controller, "Controller")}");
                                        }
                                    }

                                    template += $"{WithNoSuffix(actionMatchConfig.Controller, "Controller")}/";
                                }
                                else if (firstPart.Name.Equals("action"))
                                {
                                    if (hasConstraint &&
                                        (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint)))
                                    {
                                        passConstraint =
                                            PassConstraint(actionMatchConfig.Action, routeConstraint);

                                        if (!passConstraint)
                                        {
                                            template = null;
                                            break;
                                        }
                                    }

                                    if (route.Defaults != null &&
                                        route.Defaults.TryGetValue("action", out var defaultAction) &&
                                        !string.IsNullOrEmpty(defaultAction?.ToString()))
                                    {
                                        isDefaultAction = actionMatchConfig.Action
                                                          .Equals(defaultAction.ToString(),
                                                                  StringComparison.InvariantCultureIgnoreCase);
                                        if (isDefaultAction)
                                        {
                                            templateDefaults.Add("action", actionMatchConfig.Action);
                                        }
                                    }

                                    template += $"{actionMatchConfig.Action}/";
                                }
                                else if (firstPart.IsParameter)
                                {
                                    var hasActionDescParameter =
                                        HasActionDescriptorParameter(actionDescriptor, firstPart.Name, out var paramBindingInfo);

                                    if (hasActionDescParameter && hasConstraint)
                                    {
                                        var parameterInfo = actionDescriptor.Parameters.First(param =>
                                                                                              param.Name.Equals(firstPart.Name,
                                                                                                                StringComparison.InvariantCultureIgnoreCase));

                                        passConstraint =
                                            PassConstraint(parameterInfo, routeConstraint);

                                        if (passConstraint && paramBindingInfo == null)
                                        {
                                            var routeParam = route.ParsedTemplate.Parameters[paramIndex];
                                            if (routeParam.Name.Equals(firstPart.Name,
                                                                       StringComparison.CurrentCultureIgnoreCase) && !routeParam.IsOptional)
                                            {
                                                template += $"{{{firstPart.Name}}}/";
                                                break;
                                            }
                                        }

                                        if (!passConstraint && parameterInfo.BindingInfo?.BindingSource.Id != "Query")
                                        {
                                            template = null;
                                            break;
                                        }

                                        if (parameterInfo?.BindingInfo?.BindingSource.Id == "Path")
                                        {
                                            templateDefaults.Clear();
                                        }
                                    }

                                    if (hasActionDescParameter && paramBindingInfo != null)
                                    {
                                        actionParametersUsed.Add(firstPart.Name);
                                        template += $"{{{firstPart.Name}{(firstPart.IsOptional ? "?" : string.Empty)}}}/";
                                    }
                                    else if (!firstPart.IsOptional)
                                    {
                                        if (hasConstraint && IsCustomConstraint(routeConstraint) ||
                                            route.ParsedTemplate.Parameters.IndexOf(firstPart) != route.ParsedTemplate.Parameters.Count - 1)
                                        {
                                            template += $"{{{firstPart.Name}}}/";
                                        }
                                        else
                                        {
                                            template = null;
                                            break;
                                        }
                                    }
                                    else if (actionDescriptor.Parameters.Count > 0)
                                    {
                                        var httpMethod = actionDescriptor.ActionConstraints
                                                         .FirstOrDefault(c => c.GetType() == typeof(HttpMethodActionConstraint));

                                        if (httpMethod != null &&
                                            ((HttpMethodActionConstraint)httpMethod).HttpMethods.Count() == 1 &&
                                            ((HttpMethodActionConstraint)httpMethod).HttpMethods.First().Equals("GET"))
                                        {
                                            var unusedParameters = actionDescriptor.Parameters.Where(param =>
                                                                                                     !actionParametersUsed.Contains(param.Name));

                                            if (!string.IsNullOrEmpty(template) &&
                                                unusedParameters.All(param => param.BindingInfo == null))
                                            {
                                                break;
                                            }

                                            template = null;
                                            break;
                                        }
                                    }

                                    paramIndex++;
                                }
                            }
                        }

                        // exit on first route match
                        if (!string.IsNullOrEmpty(template))
                        {
                            break;
                        }
                    }
                }
            }

            if (!string.IsNullOrEmpty(template))
            {
                template = template.TrimEnd('/');
            }

            if (!string.IsNullOrEmpty(template) && options.SkipDefaults && templateDefaults.Any())
            {
                template = SkipDefaultsFromTemplate(templateDefaults, template);
            }

            return(template);
        }