private boolean match0(Map properties) {
    switch (op) {
      case AND:
        {
          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            if (!filters[i].match0(properties)) {
              return false;
            }
          }

          return true;
        }

      case OR:
        {
          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            if (filters[i].match0(properties)) {
              return true;
            }
          }

          return false;
        }

      case NOT:
        {
          FilterImpl filter = (FilterImpl) value;

          return !filter.match0(properties);
        }

      case SUBSTRING:
      case EQUAL:
      case GREATER:
      case LESS:
      case APPROX:
      case SUBSET:
      case SUPERSET:
        {
          Object prop = (properties == null) ? null : properties.get(attr);

          return compare(op, prop, value);
        }

      case PRESENT:
        {
          Object prop = (properties == null) ? null : properties.get(attr);

          return prop != null;
        }
    }

    return false;
  }