content: /*
   * 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.kafka.shell.glob;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.regex.Pattern;
  
  /**
   * Implements a per-path-component glob.
   */
  public final class GlobComponent {
      private static final Logger log = LoggerFactory.getLogger(GlobComponent.class);
  
      /**
       * Returns true if the character is a special character for regular expressions.
       */
      private static boolean isRegularExpressionSpecialCharacter(char ch) {
          return switch (ch) {
              case '$', '(', ')', '+', '.', '[', ']', '^', '{', '|' -> true;
              default -> false;
          };
      }
  
      /**
       * Returns true if the character is a special character for globs.
       */
      private static boolean isGlobSpecialCharacter(char ch) {
          return switch (ch) {
              case '*', '?', '\\', '{', '}' -> true;
              default -> false;
          };
      }
  
      /**
       * Converts a glob string to a regular expression string.
       * Returns null if the glob should be handled as a literal (can only match one string).
       * Throws an exception if the glob is malformed.
       */
      static String toRegularExpression(String glob) {
          StringBuilder output = new StringBuilder("^");
          boolean literal = true;
          boolean processingGroup = false;
  
          for (int i = 0; i < glob.length(); ) {
              char c = glob.charAt(i++);
              switch (c) {
                  case '?':
                      literal = false;
                      output.append(".");
                      break;
                  case '*':
                      literal = false;
                      output.append(".*");
                      break;
                  case '\\':
                      if (i == glob.length()) {
                          output.append(c);
                      } else {
                          char next = glob.charAt(i);
                          i++;
                          if (isGlobSpecialCharacter(next) ||
                                  isRegularExpressionSpecialCharacter(next)) {
                              output.append('\\');
                          }
                          output.append(next);
                      }
                      break;
                  case '{':
                      if (processingGroup) {
                          throw new RuntimeException("Can't nest glob groups.");
                      }
                      literal = false;
                      output.append("(?:(?:");
                      processingGroup = true;
                      break;
                  case ',':
                      if (processingGroup) {
                          literal = false;
                          output.append(")|(?:");
                      } else {
                          output.append(c);
                      }
                      break;
                  case '}':
                      if (processingGroup) {
                          literal = false;
                          output.append("))");
                          processingGroup = false;
                      } else {
                          output.append(c);
                      }
                      break;
                  // TODO: handle character ranges
                  default:
                      if (isRegularExpressionSpecialCharacter(c)) {
                          output.append('\\');
                      }
                      output.append(c);
              }
          }
          if (processingGroup) {
              throw new RuntimeException("Unterminated glob group.");
          }
          if (literal) {
              return null;
          }
          output.append('$');
          return output.toString();
      }
  
      private final String component;
      private final Pattern pattern;
  
      public GlobComponent(String component) {
          this.component = component;
          Pattern newPattern = null;
          try {
              String regularExpression = toRegularExpression(component);
              if (regularExpression != null) {
                  newPattern = Pattern.compile(regularExpression);
              }
          } catch (RuntimeException e) {
              log.debug("Invalid glob pattern: " + e.getMessage());
          }
          this.pattern = newPattern;
      }
  
      public String component() {
          return component;
      }
  
      public boolean literal() {
          return pattern == null;
      }
  
      public boolean matches(String nodeName) {
          if (pattern == null) {
              return component.equals(nodeName);
          } else {
              return pattern.matcher(nodeName).matches();
          }
      }
  }
  