<commit_before>package git.path.matcher.path;

import git.path.NameMatcher;
import git.path.PathMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Objects;

/**
 * Complex full-feature pattern matcher.
 *
 * @author <NAME> <<EMAIL>>
 */
public final class RecursivePathMatcher implements PathMatcher {
  @NotNull
  private final static int[] START_ARRAY = {0};
  @NotNull
  private final int[] indexes;
  @NotNull
  private final NameMatcher[] nameMatchers;
  private final boolean exact;

  public RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact) {
    this(nameMatchers, exact, START_ARRAY);
  }

  private RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact, @NotNull int[] indexes) {
    this.nameMatchers = nameMatchers;
    this.exact = exact;
    this.indexes = indexes;
  }

  @Nullable
  @Override
  public PathMatcher createChild(@NotNull String name, boolean isDir) {
    final int[] childs = new int[indexes.length * 2];
    boolean changed = false;
    int count = 0;
    for (int index : indexes) {
      if (nameMatchers[index].isMatch(name, isDir)) {
        if (nameMatchers[index].isRecursive()) {
          childs[count++] = index;
          if (nameMatchers[index + 1].isMatch(name, isDir)) {
            if (index + 2 == nameMatchers.length) {
              return exact && isDir ? null : AlwaysMatcher.INSTANCE;
            }
            childs[count++] = index + 2;
            changed = true;
          }
        } else {
          if (index + 1 == nameMatchers.length) {
            return exact && isDir ? null : AlwaysMatcher.INSTANCE;
          }
          childs[count++] = index + 1;
          changed = true;
        }
      } else {
        changed = true;
      }
    }
    if (!isDir) {
      return null;
    }
    if (!changed) {
      return this;
    }
    return count == 0 ? null : new RecursivePathMatcher(nameMatchers, exact, Arrays.copyOf(childs, count));
  }

  @Override
  public boolean isMatch() {
    return false;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    RecursivePathMatcher that = (RecursivePathMatcher) o;

    if (indexes.length != that.indexes.length) return false;
    final int offset = indexes[0];
    final int thatOffset = that.indexes[0];
    if (nameMatchers.length - offset != that.nameMatchers.length - thatOffset) return false;
<commit_msg>Fix directory pattern matching<commit_after>package git.path.matcher.path;

import git.path.NameMatcher;
import git.path.PathMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Objects;

/**
 * Complex full-feature pattern matcher.
 *
 * @author <NAME> <<EMAIL>>
 */
public final class RecursivePathMatcher implements PathMatcher {
  @NotNull
  private final static int[] START_ARRAY = {0};
  @NotNull
  private final int[] indexes;
  @NotNull
  private final NameMatcher[] nameMatchers;
  private final boolean exact;

  public RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact) {
    this(nameMatchers, exact, START_ARRAY);
  }

  private RecursivePathMatcher(@NotNull NameMatcher[] nameMatchers, boolean exact, @NotNull int[] indexes) {
    this.nameMatchers = nameMatchers;
    this.exact = exact;
    this.indexes = indexes;
  }

  @Nullable
  @Override
  public PathMatcher createChild(@NotNull String name, boolean isDir) {
    final int[] childs = new int[indexes.length * 2];
    boolean changed = false;
    int count = 0;
    for (int index : indexes) {
      if (index < nameMatchers.length && nameMatchers[index].isMatch(name, isDir)) {
        if (nameMatchers[index].isRecursive()) {
          childs[count++] = index;
          if (nameMatchers[index + 1].isMatch(name, isDir)) {
            if (index + 2 == nameMatchers.length) {
              if (!exact || !isDir) {
                return AlwaysMatcher.INSTANCE;
              }
            }
            childs[count++] = index + 2;
            changed = true;
          }
        } else {
          if (index + 1 == nameMatchers.length) {
            if (!exact || !isDir) {
              return AlwaysMatcher.INSTANCE;
            }
          }
          childs[count++] = index + 1;
          changed = true;
        }
      } else {
        changed = true;
      }
    }
    if (!isDir) {
      return null;
    }
    if (!changed) {
      return this;
    }
    return count == 0 ? null : new RecursivePathMatcher(nameMatchers, exact, Arrays.copyOf(childs, count));
  }

  @Override
  public boolean isMatch() {
    for (int index : indexes) {
      if (index == nameMatchers.length) {
        return true;
      }
    }
    return false;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    RecursivePathMatcher that = (RecursivePathMatcher) o;

    if (indexes.length != that.indexes.length) return false;
    final int offset = indexes[0];
    final int thatOffset = that.indexes[0];
    if (nameMatchers.length - offset != that.nameMatchers.length - thatOffset) return false;

{'fraction_non_alphanumeric': 0.11508758711621775, 'fraction_numerical': 0.004143906573742701, 'mean_word_length': 2.6545079146593253, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '935999', 'n_tokens_mistral': 1610, 'n_tokens_neox': 1563, 'n_words': 422}