content: package com.elasticsearch.cloud.monitor.metric.common.rule.filter;
  
  import com.elasticsearch.cloud.monitor.metric.common.utils.Utils;
  import com.fasterxml.jackson.annotation.JsonIgnore;
  import com.google.common.base.Objects;
  
  import java.util.Arrays;
  import java.util.Map;
  
  /**
   * Performs basic wild card searching. It supports prefix, postfix, infix,
   * multi-infix and case insensitive matching. The wildcard character is
   * an asterisk. If case insensitivity is enabled, we simply drop everything
   * to lower case.
   *
   * @since 2.2
   */
  public class TagVWildcardFilter extends TagVFilter {
  
      /**
       * Name of this filter
       */
      final public static String FILTER_NAME = "wildcard";
  
      /**
       * Whether or not the filter had a postfix asterisk
       */
      protected final boolean has_postfix;
  
      /**
       * Whether or not the filter had a prefix asterisk
       */
      protected final boolean has_prefix;
  
      /**
       * The individual components to match on
       */
      protected final String[] components;
  
      /**
       * Whether or not we'll match case
       */
      protected boolean case_insensitive;
  
      /**
       * The default Ctor that disables case insensitivity
       *
       * @param tagk   The tag key to associate with this filter
       * @param filter The wildcard filter to match on
       * @throws IllegalArgumentException if the tagk or filter were empty or null
       */
      public TagVWildcardFilter(final String tagk, final String filter) {
          this(tagk, filter, false);
      }
  
      /**
       * A ctor that allows enabling case insensitivity
       *
       * @param tagk             The tag key to associate with this filter
       * @param filter           The wildcard filter to match on
       * @param case_insensitive Whether or not to match on case
       * @throws IllegalArgumentException if the tagk or filter were empty or null
       */
      public TagVWildcardFilter(final String tagk, final String filter, final boolean case_insensitive) {
          super(tagk, filter);
          this.case_insensitive = case_insensitive;
  
          if (filter == null || filter.length() < 1) {
              throw new IllegalArgumentException("Filter cannot be null or empty");
          }
          String actual = case_insensitive ? filter.toLowerCase() : filter;
          if (actual.charAt(0) == '*') {
              has_postfix = true;
              while (actual.charAt(0) == '*') {
                  if (actual.length() < 2) {
                      break;
                  }
                  actual = actual.substring(1);
              }
          } else {
              has_postfix = false;
          }
          if (actual.charAt(actual.length() - 1) == '*') {
              has_prefix = true;
              while (actual.charAt(actual.length() - 1) == '*') {
                  if (actual.length() < 2) {
                      break;
                  }
                  actual = actual.substring(0, actual.length() - 1);
              }
          } else {
              has_prefix = false;
          }
          if (actual.indexOf('*') > 0) {
              components = Utils.splitString(actual, '*');
          } else {
              components = new String[1];
              components[0] = actual;
          }
  
          // avoid resolving UIDs at scan time
          if (components.length == 1 && components[0].equals("*")) {
          }
      }
  
      @Override
      public boolean match(final Map<String, String> tags) {
          String tagv = tags.get(tagk);
          if (tagv == null) {
              return false;
          } else if (components.length == 1 && components[0].equals("*")) {
              // match all
              return true;
          } else if (case_insensitive) {
              tagv = tags.get(tagk).toLowerCase();
          }
          if (has_postfix && !has_prefix && !tagv.endsWith(components[components.length - 1])) {
              return false;
          }
          if (has_prefix && !has_postfix && !tagv.startsWith(components[0])) {
              return false;
          }
          int idx = 0;
          for (int i = 0; i < components.length; i++) {
              if (tagv.indexOf(components[i], idx) < 0) {
                  return false;
              }
              idx += components[i].length();
          }
          return true;
      }
  
      @Override
      public String debugInfo() {
          return "{components=" + Arrays.toString(components) + ", case=" + case_insensitive + "}";
      }
  
      /**
       * @return Whether or not this filter has case insensitivity enabled
       */
      @JsonIgnore
      public boolean isCaseInsensitive() {
          return case_insensitive;
      }
  
      @Override
      public boolean equals(final Object obj) {
          if (obj == null) {
              return false;
          }
          if (!(obj instanceof TagVWildcardFilter)) {
              return false;
          }
          if (obj == this) {
              return true;
          }
          final TagVWildcardFilter filter = (TagVWildcardFilter) obj;
          return Objects.equal(tagk, filter.tagk) && Arrays.equals(components, filter.components) && Objects.equal(
              case_insensitive, filter.case_insensitive);
      }
  
      @Override
      public int hashCode() {
          return Objects.hashCode(tagk, Arrays.hashCode(components), case_insensitive);
      }
  
      @Override
      public String getType() {
          return FILTER_NAME;
      }
  
      /**
       * @return a string describing the filter
       */
      public static String description() {
          return "Performs pre, post and in-fix glob matching of values. The globs "
              + "are case sensitive and multiple wildcards can be used. The wildcard "
              + "character is the * (asterisk). At least one wildcard must be "
              + "present in the filter value. A wildcard by itself can be used as "
              + "well to match on any value for the tag key.";
      }
  
      /**
       * @return a list of examples showing how to use the filter
       */
      public static String examples() {
          return "host=wildcard(web*),  host=wildcard(web*.tsdb.net)  " + "{\"type\":\"wildcard\",\"tagk\":\"host\","
              + "\"filter\":\"web*.tsdb.net\",\"groupBy\":false}";
      }
  
      /**
       * Case insensitive version
       */
      public static class TagVIWildcardFilter extends TagVWildcardFilter {
          /**
           * Name of this filter
           */
          final public static String FILTER_NAME = "iwildcard";
  
          public TagVIWildcardFilter(final String tagk, final String filter) {
              super(tagk, filter, true);
          }
  
          @Override
          public String getType() {
              return FILTER_NAME;
          }
  
          /**
           * @return a string describing the filter
           */
          public static String description() {
              return "Performs pre, post and in-fix glob matching of values. The globs "
                  + "are case insensitive and multiple wildcards can be used. The wildcard "
                  + "character is the * (asterisk). Case insensitivity is achieved by "
                  + "dropping all values to lower case. At least one wildcard must be "
                  + "present in the filter value. A wildcard by itself can be used as "
                  + "well to match on any value for the tag key.";
          }
  
          /**
           * @return a list of examples showing how to use the filter
           */
          public static String examples() {
              return "host=iwildcard(web*),  host=iwildcard(web*.tsdb.net)  "
                  + "{\"type\":\"iwildcard\",\"tagk\":\"host\"," + "\"filter\":\"web*.tsdb.net\",\"groupBy\":false}";
          }
      }
  }
  