code: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
  
  package org.apache.skywalking.oap.server.core.config.group.openapi;
  
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.Map;
  import lombok.Getter;
  import org.apache.skywalking.oap.server.library.util.StringFormatGroup;
  
  public class EndpointGroupingRule4Openapi {
      private final Map<String/*serviceName*/, Map<String/*endpointName*/, String/*endpointGroupName*/>> directLookup = new HashMap<>();
      @Getter
      private final Map<String, Map<String, StringFormatGroup>> groupedRules = new HashMap<>();
  
      void addDirectLookup(String serviceName, String endpointName, String endpointGroupName) {
          Map<String, String> endpointNameLookup = directLookup.computeIfAbsent(serviceName, name -> new HashMap<>());
          endpointNameLookup.put(endpointName, endpointGroupName);
      }
  
      void addGroupedRule(String serviceName, String endpointGroupName, String ruleRegex) {
          String rulesGroupkey = getGroupedRulesKey(ruleRegex);
          Map<String, StringFormatGroup> rules = groupedRules.computeIfAbsent(serviceName, name -> new HashMap<>());
          StringFormatGroup formatGroup = rules.computeIfAbsent(rulesGroupkey, name -> new StringFormatGroup());
          formatGroup.addRule(endpointGroupName, ruleRegex);
      }
  
      public StringFormatGroup.FormatResult format(String service, String endpointName) {
          Map<String, String> endpointNameLookup = directLookup.get(service);
          if (endpointNameLookup != null && endpointNameLookup.get(endpointName) != null) {
              return new StringFormatGroup.FormatResult(true, endpointName, endpointNameLookup.get(endpointName));
          }
  
          Map<String, StringFormatGroup> rules = groupedRules.get(service);
          if (rules != null) {
              final StringFormatGroup stringFormatGroup = rules.get(getGroupedRulesKey(endpointName));
              if (stringFormatGroup != null) {
                  return stringFormatGroup.format(endpointName);
              }
          }
  
          return new StringFormatGroup.FormatResult(false, endpointName, endpointName);
      }
  
      void sortRulesAll() {
          groupedRules.entrySet().forEach(rules -> {
              sortRulesByService(rules.getKey());
          });
      }
  
      void sortRulesByService(String serviceName) {
          Map<String, StringFormatGroup> rules = groupedRules.get(serviceName);
          if (rules != null) {
              rules.entrySet().forEach(stringFormatGroup -> {
                  stringFormatGroup.getValue()
                                   .sortRules(new EndpointGroupingRule4Openapi.EndpointGroupingRulesComparator());
              });
          }
      }
  
      String getGroupedRulesKey(String string) {
          String[] ss = string.split("/");
          if (ss.length == 1) {   //eg. POST:/
              return ss[0] + "/";
          }
          if (ss.length > 1) {
              return ss[0] + "/" + ss[1];
          }
          return "/";
      }
  
      static class EndpointGroupingRulesComparator implements Comparator<StringFormatGroup.PatternRule> {
          private static final String VAR_PATTERN = "\\(\\[\\^\\/\\]\\+\\)";
  
          @Override
          public int compare(final StringFormatGroup.PatternRule rule1, final StringFormatGroup.PatternRule rule2) {
  
              String pattern1 = rule1.getPattern().pattern();
              String pattern2 = rule2.getPattern().pattern();
  
              if (getPatternVarsCount(pattern1) < getPatternVarsCount(pattern2)) {
                  return -1;
              } else if (getPatternVarsCount(pattern1) > getPatternVarsCount(pattern2)) {
                  return 1;
              }
  
              int length1 = getPatternLength(pattern1);
              int length2 = getPatternLength(pattern2);
              return length2 - length1;
          }
  
          private int getPatternVarsCount(String pattern) {
              return ",".concat(pattern).concat(",").split(VAR_PATTERN).length - 1;
          }
  
          private int getPatternLength(String pattern) {
              return pattern.replaceAll(VAR_PATTERN, "#").length();
          }
  
      }
  }
  