content: package com.alibaba.tesla.gateway.server.gateway;
  
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  
  import com.alibaba.fastjson.JSONObject;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory;
  import org.springframework.cloud.gateway.route.Route;
  import org.springframework.cloud.gateway.route.RouteDefinition;
  import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
  import org.springframework.cloud.gateway.route.RouteLocator;
  import reactor.core.publisher.Flux;
  
  import org.springframework.beans.BeansException;
  import org.springframework.beans.factory.BeanFactory;
  import org.springframework.beans.factory.BeanFactoryAware;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.cloud.gateway.config.GatewayProperties;
  import org.springframework.cloud.gateway.event.FilterArgsEvent;
  import org.springframework.cloud.gateway.event.PredicateArgsEvent;
  import org.springframework.cloud.gateway.filter.FilterDefinition;
  import org.springframework.cloud.gateway.filter.GatewayFilter;
  import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
  import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
  import org.springframework.cloud.gateway.handler.AsyncPredicate;
  import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
  import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
  import org.springframework.cloud.gateway.support.ConfigurationUtils;
  import org.springframework.cloud.gateway.support.HasRouteId;
  import org.springframework.context.ApplicationEventPublisher;
  import org.springframework.context.ApplicationEventPublisherAware;
  import org.springframework.core.Ordered;
  import org.springframework.core.annotation.AnnotationAwareOrderComparator;
  import org.springframework.core.convert.ConversionService;
  import org.springframework.expression.spel.standard.SpelExpressionParser;
  import org.springframework.validation.Validator;
  import org.springframework.web.server.ServerWebExchange;
  
  /**
   * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}.
   *
   * @author Spencer Gibb
   */
  public class TeslaRouteDefinitionRouteLocator
      implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
  
      /**
       * Default filters name.
       */
      public static final String DEFAULT_FILTERS = "defaultFilters";
  
      protected final Log logger = LogFactory.getLog(getClass());
  
      private final RouteDefinitionLocator routeDefinitionLocator;
  
      private final ConversionService conversionService;
  
      private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
  
      private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
  
      private final GatewayProperties gatewayProperties;
  
      private final SpelExpressionParser parser = new SpelExpressionParser();
  
      private BeanFactory beanFactory;
  
      private ApplicationEventPublisher publisher;
  
      @Autowired
      private Validator validator;
  
      public TeslaRouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
                                              List<RoutePredicateFactory> predicates,
                                              List<GatewayFilterFactory> gatewayFilterFactories,
                                              GatewayProperties gatewayProperties, ConversionService conversionService) {
          this.routeDefinitionLocator = routeDefinitionLocator;
          this.conversionService = conversionService;
          initFactories(predicates);
          gatewayFilterFactories.forEach(
              factory -> this.gatewayFilterFactories.put(factory.name(), factory));
          this.gatewayProperties = gatewayProperties;
      }
  
      @Override
      public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
          this.beanFactory = beanFactory;
      }
  
      @Override
      public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
          this.publisher = publisher;
      }
  
      private void initFactories(List<RoutePredicateFactory> predicates) {
          predicates.forEach(factory -> {
              String key = factory.name();
              if (this.predicates.containsKey(key)) {
                  this.logger.warn("A RoutePredicateFactory named " + key
                      + " already exists, class: " + this.predicates.get(key)
                      + ". It will be overwritten.");
              }
              this.predicates.put(key, factory);
              if (logger.isInfoEnabled()) {
                  logger.info("Loaded RoutePredicateFactory [" + key + "]");
              }
          });
      }
  
      @Override
      public Flux<Route> getRoutes() {
          return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
              // TODO: error handling
              .map(route -> {
                  if (logger.isDebugEnabled()) {
                      logger.debug("RouteDefinition matched: " + route.getId());
                  }
                  return route;
              });
  
          /*
           * TODO: trace logging if (logger.isTraceEnabled()) {
           * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
           */
      }
  
      private Route convertToRoute(RouteDefinition routeDefinition) {
          AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
          List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
  
          return Route.async(routeDefinition).asyncPredicate(predicate)
              .replaceFilters(gatewayFilters).build();
      }
  
      @SuppressWarnings("unchecked")
      List<GatewayFilter> loadGatewayFilters(String id,
                                             List<FilterDefinition> filterDefinitions) {
          ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
          for (int i = 0; i < filterDefinitions.size(); i++) {
              FilterDefinition definition = filterDefinitions.get(i);
              GatewayFilterFactory factory = this.gatewayFilterFactories
                  .get(definition.getName());
              if (factory == null) {
                  throw new IllegalArgumentException(
                      "Unable to find GatewayFilterFactory with name "
                          + definition.getName());
              }
              Map<String, String> args = definition.getArgs();
              if (logger.isDebugEnabled()) {
                  logger.debug("RouteDefinition " + id + " applying filter " + args + " to "
                      + definition.getName());
              }
  
              Map<String, Object> properties = factory.shortcutType().normalize(args,
                  factory, this.parser, this.beanFactory);
  
              Object configuration = factory.newConfig();
  
              ConfigurationUtils.bind(configuration, properties,
                  factory.shortcutFieldPrefix(), definition.getName(), validator);
  
              // some filters require routeId
              // TODO: is there a better place to apply this?
              if (configuration instanceof HasRouteId) {
                  HasRouteId hasRouteId = (HasRouteId) configuration;
                  hasRouteId.setRouteId(id);
              }
              if(configuration instanceof StripPrefixGatewayFilterFactory.Config){
                  int parts = ((StripPrefixGatewayFilterFactory.Config)configuration).getParts();
                  String genKey = definition.getArgs().get("_genkey_0");
                  if(StringUtils.isNotBlank(genKey) && StringUtils.isNumeric(genKey)){
                      int value = Integer.parseInt(genKey);
                      if(parts != value){
                          logger.warn("genkey to parts failed, genkey_0=" + genKey + "parts=" + parts);
                          ((StripPrefixGatewayFilterFactory.Config)configuration).setParts(value);
                      }
                  }else {
                      logger.error("genkey_0 failed, routId=" + id + ", filterDefine=" + definition.toString());
                  }
              }
  
              GatewayFilter gatewayFilter = factory.apply(configuration);
              if (this.publisher != null) {
                  this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
              }
              if (gatewayFilter instanceof Ordered) {
                  ordered.add(gatewayFilter);
              }
              else {
                  ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
              }
          }
  
          return ordered;
      }
  
      private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
          List<GatewayFilter> filters = new ArrayList<>();
  
          // TODO: support option to apply defaults after route specific filters?
          if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
              filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
                  this.gatewayProperties.getDefaultFilters()));
          }
  
          if (!routeDefinition.getFilters().isEmpty()) {
              filters.addAll(loadGatewayFilters(routeDefinition.getId(),
                  routeDefinition.getFilters()));
          }
  
          AnnotationAwareOrderComparator.sort(filters);
          return filters;
      }
  
      private AsyncPredicate<ServerWebExchange> combinePredicates(
          RouteDefinition routeDefinition) {
          List<PredicateDefinition> predicates = routeDefinition.getPredicates();
          AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
              predicates.get(0));
  
          for (PredicateDefinition andPredicate : predicates.subList(1,
              predicates.size())) {
              AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
                  andPredicate);
              predicate = predicate.and(found);
          }
  
          return predicate;
      }
  
      @SuppressWarnings("unchecked")
      private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
                                                       PredicateDefinition predicate) {
          RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
          if (factory == null) {
              throw new IllegalArgumentException(
                  "Unable to find RoutePredicateFactory with name "
                      + predicate.getName());
          }
          Map<String, String> args = predicate.getArgs();
          if (logger.isDebugEnabled()) {
              logger.debug("RouteDefinition " + route.getId() + " applying " + args + " to "
                  + predicate.getName());
          }
  
          Map<String, Object> properties = factory.shortcutType().normalize(args, factory,
              this.parser, this.beanFactory);
          Object config = factory.newConfig();
          ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(),
              predicate.getName(), validator, conversionService);
          if (this.publisher != null) {
              this.publisher.publishEvent(
                  new PredicateArgsEvent(this, route.getId(), properties));
          }
          return factory.applyAsync(config);
      }
  
  }