docs: private static Filter convertToCNFInternal(Filter current)
    {
      if (current instanceof NotFilter) {
        return new NotFilter(convertToCNFInternal(((NotFilter) current).getBaseFilter()));
      }
      if (current instanceof AndFilter) {
        List<Filter> children = new ArrayList<>();
        for (Filter child : ((AndFilter) current).getFilters()) {
          children.add(convertToCNFInternal(child));
        }
        return new AndFilter(children);
      }
      if (current instanceof OrFilter) {
        // a list of leaves that weren't under AND expressions
        List<Filter> nonAndList = new ArrayList<Filter>();
        // a list of AND expressions that we need to distribute
        List<Filter> andList = new ArrayList<Filter>();
        for (Filter child : ((OrFilter) current).getFilters()) {
          if (child instanceof AndFilter) {
            andList.add(child);
          } else if (child instanceof OrFilter) {
            // pull apart the kids of the OR expression
            for (Filter grandChild : ((OrFilter) child).getFilters()) {
              nonAndList.add(grandChild);
            }
          } else {
            nonAndList.add(child);
          }
        }
        if (!andList.isEmpty()) {
          List<Filter> result = new ArrayList<>();
          generateAllCombinations(result, andList, nonAndList);
          return new AndFilter(result);
        }
      }
      return current;
    }