content: package com.alibaba.fastjson2;
  
  import com.alibaba.fastjson2.util.Fnv;
  import com.alibaba.fastjson2.util.IOUtils;
  import com.alibaba.fastjson2.util.TypeUtils;
  import com.alibaba.fastjson2.writer.FieldWriter;
  import com.alibaba.fastjson2.writer.ObjectWriter;
  import com.alibaba.fastjson2.writer.ObjectWriterAdapter;
  
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.util.*;
  import java.util.function.BiFunction;
  import java.util.function.Function;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  import java.util.stream.Collectors;
  
  abstract class JSONPathFilter
          extends JSONPathSegment
          implements JSONPathSegment.EvalSegment {
      private boolean and = true;
  
      public boolean isAnd() {
          return and;
      }
  
      public JSONPathFilter setAnd(boolean and) {
          this.and = and;
          return this;
      }
  
      abstract boolean apply(JSONPath.Context context, Object object);
  
      enum Operator {
          EQ,
          NE,
          GT,
          GE,
          LT,
          LE,
          LIKE,
          NOT_LIKE,
          RLIKE,
          NOT_RLIKE,
          IN,
          NOT_IN,
          BETWEEN,
          NOT_BETWEEN,
          AND,
          OR,
          REG_MATCH,
          STARTS_WITH,
          ENDS_WITH,
          CONTAINS,
          NOT_CONTAINS;
  
          public String toString() {
              switch (this) {
                  case EQ:
                      return "==";
                  case NE:
                      return "!=";
                  case GT:
                      return ">";
                  case GE:
                      return ">=";
                  case LT:
                      return "<";
                  case LE:
                      return "<=";
                  case LIKE:
                      return "like";
                  case NOT_LIKE:
                      return "not like";
                  case RLIKE:
                      return "rlike";
                  case NOT_RLIKE:
                      return "not rlike";
                  case BETWEEN:
                      return "between";
                  case NOT_BETWEEN:
                      return "not between";
                  case AND:
                      return "and";
                  case OR:
                      return "or";
                  case STARTS_WITH:
                      return "starts with";
                  case ENDS_WITH:
                      return "ends with";
                  case CONTAINS:
                      return "contains";
                  case NOT_CONTAINS:
                      return "not contains";
                  default:
                      return name();
              }
          }
      }
  
      static final class NameIsNull
              extends NameFilter {
          public NameIsNull(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function function
          ) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, function);
          }
  
          protected boolean applyNull() {
              return true;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              if (function != null) {
                  fieldValue = function.apply(fieldValue);
              }
              return fieldValue == null;
          }
      }
  
      static final class NameIntOpSegment
              extends NameFilter {
          final Operator operator;
          final long value;
  
          public NameIntOpSegment(
                  String name,
                  long nameHashCode,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function expr,
                  Operator operator,
                  long value) {
              super(name, nameHashCode, fieldName2, fieldNameNameHash2, expr);
              this.operator = operator;
              this.value = value;
          }
  
          @Override
          protected boolean applyNull() {
              return operator == Operator.NE;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              boolean objInt = fieldValue instanceof Boolean
                      || fieldValue instanceof Byte
                      || fieldValue instanceof Short
                      || fieldValue instanceof Integer
                      || fieldValue instanceof Long;
              if (objInt) {
                  long fieldValueInt;
                  if (fieldValue instanceof Boolean) {
                      fieldValueInt = (Boolean) fieldValue ? 1 : 0;
                  } else {
                      fieldValueInt = ((Number) fieldValue).longValue();
                  }
  
                  switch (operator) {
                      case LT:
                          return fieldValueInt < this.value;
                      case LE:
                          return fieldValueInt <= this.value;
                      case EQ:
                          return fieldValueInt == this.value;
                      case NE:
                          return fieldValueInt != this.value;
                      case GT:
                          return fieldValueInt > this.value;
                      case GE:
                          return fieldValueInt >= this.value;
                      default:
                          throw new UnsupportedOperationException();
                  }
              }
  
              int cmp;
              if (fieldValue instanceof BigDecimal) {
                  cmp = ((BigDecimal) fieldValue)
                          .compareTo(
                                  BigDecimal.valueOf(value));
              } else if (fieldValue instanceof BigInteger) {
                  cmp = ((BigInteger) fieldValue)
                          .compareTo(
                                  BigInteger.valueOf(value));
              } else if (fieldValue instanceof Float) {
                  cmp = ((Float) fieldValue)
                          .compareTo(
                                  (float) value);
              } else if (fieldValue instanceof Double) {
                  cmp = ((Double) fieldValue)
                          .compareTo(
                                  (double) value);
              } else if (fieldValue instanceof String) {
                  String fieldValueStr = (String) fieldValue;
                  if (IOUtils.isNumber(fieldValueStr)) {
                      try {
                          cmp = Long.compare(Long.parseLong(fieldValueStr), value);
                      } catch (Exception ignored) {
                          cmp = fieldValueStr.compareTo(Long.toString(value));
                      }
                  } else {
                      cmp = fieldValueStr.compareTo(Long.toString(value));
                  }
              } else {
                  throw new UnsupportedOperationException();
              }
  
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
  
          @Override
          public void set(JSONPath.Context context, Object value) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          list.set(i, value);
                      }
                  }
                  return;
              }
  
              throw new JSONException("UnsupportedOperation ");
          }
  
          public void setCallback(JSONPath.Context context, BiFunction callback) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          Object value = callback.apply(list, item);
                          if (value != item) {
                              list.set(i, value);
                          }
                      }
                  }
                  return;
              }
  
              throw new JSONException("UnsupportedOperation ");
          }
  
          @Override
          public String toString() {
              return "[?("
                      + (fieldName2 == null ? "@" : fieldName2)
                      + '.'
                      + fieldName + ' ' + operator + ' ' + value
                      + ")]";
          }
      }
  
      static final class NameDecimalOpSegment
              extends NameFilter {
          final Operator operator;
          final BigDecimal value;
  
          public NameDecimalOpSegment(String name, long nameHashCode, Operator operator, BigDecimal value) {
              super(name, nameHashCode);
              this.operator = operator;
              this.value = value;
          }
  
          protected boolean applyNull() {
              return operator == Operator.NE;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue == null) {
                  return false;
              }
  
              BigDecimal fieldValueDecimal;
  
              if (fieldValue instanceof Boolean) {
                  fieldValueDecimal = (Boolean) fieldValue ? BigDecimal.ONE : BigDecimal.ZERO;
              } else if (fieldValue instanceof Byte
                      || fieldValue instanceof Short
                      || fieldValue instanceof Integer
                      || fieldValue instanceof Long) {
                  fieldValueDecimal = BigDecimal.valueOf(
                          ((Number) fieldValue).longValue()
                  );
              } else if (fieldValue instanceof BigDecimal) {
                  fieldValueDecimal = ((BigDecimal) fieldValue);
              } else if (fieldValue instanceof BigInteger) {
                  fieldValueDecimal = new BigDecimal((BigInteger) fieldValue);
              } else {
                  throw new UnsupportedOperationException();
              }
  
              int cmp = fieldValueDecimal.compareTo(value);
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
      }
  
      static final class NameDoubleOpSegment
              extends NameFilter {
          final Operator operator;
          final double value;
  
          public NameDoubleOpSegment(String name, long nameHashCode, Operator operator, Double value) {
              super(name, nameHashCode);
              this.operator = operator;
              this.value = value;
          }
  
          protected boolean applyNull() {
              return operator == Operator.NE;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue == null) {
                  return false;
              }
  
              Double fieldValueDouble;
  
              if (fieldValue instanceof Boolean) {
                  fieldValueDouble = (Boolean) fieldValue ? 1D : 0D;
              } else if (fieldValue instanceof Number) {
                  fieldValueDouble = ((Number) fieldValue).doubleValue();
              } else {
                  throw new UnsupportedOperationException();
              }
  
              int cmp = fieldValueDouble.compareTo(value);
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
      }
  
      static final class NameRLikeSegment
              extends NameFilter {
          final Pattern pattern;
          final boolean not;
  
          public NameRLikeSegment(String fieldName, long fieldNameNameHash, Pattern pattern, boolean not) {
              super(fieldName, fieldNameNameHash);
              this.pattern = pattern;
              this.not = not;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              String strPropertyValue = fieldValue.toString();
              Matcher m = pattern.matcher(strPropertyValue);
              boolean match = m.matches();
  
              if (not) {
                  match = !match;
              }
  
              return match;
          }
      }
  
      static final class StartsWithSegment
              extends NameFilter {
          final String prefix;
  
          public StartsWithSegment(String fieldName, long fieldNameNameHash, String prefix) {
              super(fieldName, fieldNameNameHash);
              this.prefix = prefix;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              String propertyValue = fieldValue.toString();
              return propertyValue != null && propertyValue.startsWith(prefix);
          }
      }
  
      static final class EndsWithSegment
              extends NameFilter {
          final String prefix;
  
          public EndsWithSegment(String fieldName, long fieldNameNameHash, String prefix) {
              super(fieldName, fieldNameNameHash);
              this.prefix = prefix;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              String propertyValue = fieldValue.toString();
              return propertyValue != null && propertyValue.endsWith(prefix);
          }
      }
  
      static final class GroupFilter
              extends JSONPathSegment
              implements EvalSegment {
          final List<JSONPathFilter> filters;
  
          public GroupFilter(List<JSONPathFilter> filters) {
              this.filters = filters;
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              if (context.parent == null) {
                  context.root = jsonReader.readAny();
              }
              eval(context);
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              List<JSONPathFilter> orderedFilters = new ArrayList<>();
              if (this.filters != null) {
                  orderedFilters = this.filters.stream().sorted(Comparator.comparing(JSONPathFilter::isAnd)).collect(Collectors.toList());
              }
              if (object instanceof List) {
                  List list = (List) object;
                  JSONArray array = new JSONArray(list.size());
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      boolean match = false;
                      for (JSONPathFilter filter : orderedFilters) {
                          boolean and = filter.isAnd();
                          match = and;
                          boolean result = filter.apply(context, item);
                          if (and) {
                              if (!result) {
                                  match = false;
                                  break;
                              }
                          } else {
                              if (result) {
                                  match = true;
                                  break;
                              }
                          }
                      }
                      if (match) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              boolean match = false;
              for (JSONPathFilter filter : orderedFilters) {
                  boolean and = filter.isAnd();
                  match = and;
                  boolean result = filter.apply(context, object);
                  if (and) {
                      if (!result) {
                          match = false;
                          break;
                      }
                  } else {
                      if (result) {
                          match = true;
                          break;
                      }
                  }
              }
              if (match) {
                  context.value = object;
              }
              context.eval = true;
          }
      }
  
      abstract static class NameFilter
              extends JSONPathFilter {
          final String fieldName;
          final long fieldNameNameHash;
  
          final String[] fieldName2;
          final long[] fieldNameNameHash2;
  
          final Function function;
  
          boolean includeArray = true;
  
          public NameFilter(String fieldName, long fieldNameNameHash) {
              this.fieldName = fieldName;
              this.fieldNameNameHash = fieldNameNameHash;
              this.fieldName2 = null;
              this.fieldNameNameHash2 = null;
              this.function = null;
          }
  
          public NameFilter(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function function
          ) {
              this.fieldName = fieldName;
              this.fieldNameNameHash = fieldNameNameHash;
              this.fieldName2 = fieldName2;
              this.fieldNameNameHash2 = fieldNameNameHash2;
              this.function = function;
          }
  
          abstract boolean apply(Object fieldValue);
  
          @Override
          public final void accept(JSONReader jsonReader, JSONPath.Context context) {
              if (context.parent == null) {
                  context.root = jsonReader.readAny();
              }
              eval(context);
          }
  
          @Override
          public boolean remove(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = list.size() - 1; i >= 0; i--) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          list.remove(i);
                      }
                  }
                  return true;
              }
  
              throw new JSONException("UnsupportedOperation " + getClass());
          }
  
          @Override
          public final void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  JSONArray array = new JSONArray(list.size());
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              if (object instanceof Object[]) {
                  Object[] list = (Object[]) object;
                  JSONArray array = new JSONArray(list.length);
                  for (Object item : list) {
                      if (apply(context, item)) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              if (object instanceof JSONPath.Sequence) {
                  JSONPath.Sequence sequence = (JSONPath.Sequence) object;
                  JSONArray array = new JSONArray();
                  for (Object value : sequence.values) {
                      if (includeArray && value instanceof Collection) {
                          for (Object valueItem : ((Collection<?>) value)) {
                              if (apply(context, valueItem)) {
                                  array.add(valueItem);
                              }
                          }
                      } else {
                          if (apply(context, value)) {
                              array.add(value);
                          }
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              if (apply(context, object)) {
                  context.value = object;
                  context.eval = true;
              }
          }
  
          protected boolean applyNull() {
              return false;
          }
  
          @Override
          public boolean apply(JSONPath.Context context, Object object) {
              if (object == null) {
                  return false;
              }
  
              JSONWriter.Context writerContext = context.path.getWriterContext();
  
              if (object instanceof Map) {
                  Object fieldValue = fieldName == null ? object : ((Map<?, ?>) object).get(fieldName);
                  if (fieldValue == null) {
                      return applyNull();
                  }
  
                  if (fieldName2 != null) {
                      for (int i = 0; i < fieldName2.length; i++) {
                          String name = fieldName2[i];
                          if (fieldValue instanceof Map) {
                              fieldValue = ((Map) fieldValue).get(name);
                          } else {
                              ObjectWriter objectWriter2 = writerContext.getObjectWriter(fieldValue.getClass());
                              if (objectWriter2 instanceof ObjectWriterAdapter) {
                                  FieldWriter fieldWriter2 = objectWriter2.getFieldWriter(fieldNameNameHash2[i]);
                                  if (fieldWriter2 == null) {
                                      return false;
                                  }
                                  fieldValue = fieldWriter2.getFieldValue(fieldValue);
                              } else {
                                  return false;
                              }
                          }
  
                          if (fieldValue == null) {
                              return this instanceof NameIsNull;
                          }
                      }
                  }
  
                  if (function != null) {
                      fieldValue = function.apply(fieldValue);
                  }
  
                  return apply(fieldValue);
              }
  
              ObjectWriter objectWriter = writerContext.getObjectWriter(object.getClass());
              if (objectWriter instanceof ObjectWriterAdapter) {
                  FieldWriter fieldWriter = objectWriter.getFieldWriter(fieldNameNameHash);
                  Object fieldValue = fieldWriter.getFieldValue(object);
  
                  if (fieldValue == null) {
                      return false;
                  }
  
                  if (fieldName2 != null) {
                      for (int i = 0; i < fieldName2.length; i++) {
                          String name = fieldName2[i];
                          if (fieldValue instanceof Map) {
                              fieldValue = ((Map) fieldValue).get(name);
                          } else {
                              ObjectWriter objectWriter2 = writerContext.getObjectWriter(fieldValue.getClass());
                              if (objectWriter2 instanceof ObjectWriterAdapter) {
                                  FieldWriter fieldWriter2 = objectWriter2.getFieldWriter(fieldNameNameHash2[i]);
                                  if (fieldWriter2 == null) {
                                      return false;
                                  }
                                  fieldValue = fieldWriter2.getFieldValue(fieldValue);
                              } else {
                                  return false;
                              }
                          }
  
                          if (fieldValue == null) {
                              return false;
                          }
                      }
                  }
  
                  if (function != null) {
                      fieldValue = function.apply(fieldValue);
                  }
  
                  return apply(fieldValue);
              }
  
              if (function != null) {
                  Object fieldValue = function.apply(object);
                  return apply(fieldValue);
              }
  
              if (fieldName == null) {
                  return apply(object);
              }
  
              return false;
          }
  
          protected void excludeArray() {
              this.includeArray = false;
          }
      }
  
      static final class NameStringOpSegment
              extends NameFilter {
          final Operator operator;
          final String value;
  
          public NameStringOpSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function expr,
                  Operator operator,
                  String value
          ) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, expr);
              this.operator = operator;
              this.value = value;
          }
  
          protected boolean applyNull() {
              return operator == Operator.NE;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (!(fieldValue instanceof String)) {
                  return false;
              }
  
              String fieldValueStr = ((String) fieldValue);
              if (operator == Operator.STARTS_WITH) {
                  return fieldValueStr.startsWith(value);
              }
              if (operator == Operator.ENDS_WITH) {
                  return fieldValueStr.endsWith(value);
              }
  
              int cmp = fieldValueStr.compareTo(this.value);
  
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
      }
  
      static final class NameArrayOpSegment
              extends NameFilter {
          final Operator operator;
          final JSONArray array;
  
          public NameArrayOpSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function function,
                  Operator operator,
                  JSONArray array
          ) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, function);
              this.operator = operator;
              this.array = array;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              if (Objects.requireNonNull(operator) == Operator.EQ) {
                  return array.equals(fieldValue);
              }
              throw new JSONException("not support operator : " + operator);
          }
      }
  
      static final class NameObjectOpSegment
              extends NameFilter {
          final Operator operator;
          final JSONObject object;
  
          public NameObjectOpSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function function,
                  Operator operator,
                  JSONObject object) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, function);
              this.operator = operator;
              this.object = object;
          }
  
          @Override
          boolean apply(Object fieldValue) {
              if (Objects.requireNonNull(operator) == Operator.EQ) {
                  return object.equals(fieldValue);
              }
              throw new JSONException("not support operator : " + operator);
          }
      }
  
      static final class NameStringInSegment
              extends NameFilter {
          private final String[] values;
          private final boolean not;
  
          public NameStringInSegment(String fieldName, long fieldNameNameHash, String[] values, boolean not) {
              super(fieldName, fieldNameNameHash);
              this.values = values;
              this.not = not;
          }
  
          protected boolean applyNull() {
              return not;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              for (String value : values) {
                  if (value == fieldValue) {
                      return !not;
                  } else if (value != null && value.equals(fieldValue)) {
                      return !not;
                  }
              }
  
              return not;
          }
      }
  
      static final class NameStringContainsSegment
              extends NameFilter {
          private final String[] values;
          private final boolean not;
  
          public NameStringContainsSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  String[] values, boolean not) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, null);
              this.values = values;
              this.not = not;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue instanceof Collection) {
                  Collection collection = (Collection) fieldValue;
  
                  boolean containsAll = true;
                  for (String value : values) {
                      if (!collection.contains(value)) {
                          containsAll = false;
                          break;
                      }
                  }
  
                  if (containsAll) {
                      return !not;
                  }
              }
  
              return not;
          }
      }
  
      static final class NameMatchFilter
              extends NameFilter {
          final String startsWithValue;
          final String endsWithValue;
          final String[] containsValues;
          final int minLength;
          final boolean not;
  
          public NameMatchFilter(
                  String fieldName,
                  long fieldNameNameHash,
                  String startsWithValue,
                  String endsWithValue,
                  String[] containsValues,
                  boolean not) {
              super(fieldName, fieldNameNameHash);
              this.startsWithValue = startsWithValue;
              this.endsWithValue = endsWithValue;
              this.containsValues = containsValues;
              this.not = not;
  
              int len = 0;
              if (startsWithValue != null) {
                  len += startsWithValue.length();
              }
  
              if (endsWithValue != null) {
                  len += endsWithValue.length();
              }
  
              if (containsValues != null) {
                  for (String item : containsValues) {
                      len += item.length();
                  }
              }
  
              this.minLength = len;
          }
  
          @Override
          boolean apply(Object arg) {
              if (!(arg instanceof String)) {
                  return false;
              }
  
              String fieldValue = (String) arg;
              if (fieldValue.length() < minLength) {
                  return not;
              }
  
              int start = 0;
              if (startsWithValue != null) {
                  if (!fieldValue.startsWith(startsWithValue)) {
                      return not;
                  }
                  start += startsWithValue.length();
              }
  
              if (containsValues != null) {
                  for (String containsValue : containsValues) {
                      int index = fieldValue.indexOf(containsValue, start);
                      if (index == -1) {
                          return not;
                      }
                      start = index + containsValue.length();
                  }
              }
  
              if (endsWithValue != null) {
                  if (!fieldValue.endsWith(endsWithValue)) {
                      return not;
                  }
              }
  
              return !not;
          }
      }
  
      static final class NameExistsFilter
              extends JSONPathFilter {
          final String name;
          final long nameHashCode;
  
          public NameExistsFilter(String name, long nameHashCode) {
              this.name = name;
              this.nameHashCode = nameHashCode;
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              JSONArray array = new JSONArray();
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (item instanceof Map) {
                          if (((Map) item).containsKey(name)) {
                              array.add(item);
                          }
                      }
                  }
                  context.value = array;
                  return;
              }
  
              if (object instanceof Map) {
                  Map map = (Map) object;
                  Object value = map.get(name);
                  context.value = value != null ? object : null;
                  return;
              }
  
              if (object instanceof JSONPath.Sequence) {
                  List list = ((JSONPath.Sequence) object).values;
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (item instanceof Map) {
                          if (((Map) item).containsKey(name)) {
                              array.add(item);
                          }
                      }
                  }
                  if (context.next != null) {
                      context.value = new JSONPath.Sequence(array);
                  } else {
                      context.value = array;
                  }
                  return;
              }
  
              throw new UnsupportedOperationException();
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              eval(context);
          }
  
          @Override
          public String toString() {
              return '?' + name;
          }
  
          @Override
          public boolean apply(JSONPath.Context context, Object object) {
              if (object instanceof Map) {
                  return ((Map) object).containsKey(name);
              }
              throw new UnsupportedOperationException();
          }
      }
  
      static final class NamesExistsFilter
              extends JSONPathFilter {
          final String[] names;
          final long[] nameHashCodes;
  
          public NamesExistsFilter(List<String> names) {
              this.names = names.toArray(new String[0]);
              long[] nameHashCodes = new long[this.names.length];
              for (int i = 0; i < nameHashCodes.length; i++) {
                  nameHashCodes[i] = Fnv.hashCode64(this.names[i]);
              }
              this.nameHashCodes = nameHashCodes;
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object first = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              Object object = first;
              for (int i = 0; i < names.length; i++) {
                  String name = names[i];
                  if (object instanceof Map) {
                      Map map = (Map) object;
                      Object value = map.get(name);
                      if (i == names.length - 1 || value == null) {
                          context.value = value != null ? first : null;
                          return;
                      }
                      object = value;
                  }
              }
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              eval(context);
          }
  
          @Override
          public String toString() {
              StringBuilder buf = new StringBuilder("exists(@");
              for (int i = 0; i < names.length; i++) {
                  buf.append('.');
                  buf.append(names[i]);
              }
              buf.append(')');
              return buf.toString();
          }
  
          @Override
          public boolean apply(JSONPath.Context context, Object object) {
              throw new UnsupportedOperationException();
          }
      }
  
      static final class NameIntBetweenSegment
              extends NameFilter {
          private final long begin;
          private final long end;
          private final boolean not;
  
          public NameIntBetweenSegment(String fieldName, long fieldNameNameHash, long begin, long end, boolean not) {
              super(fieldName, fieldNameNameHash);
              this.begin = begin;
              this.end = end;
              this.not = not;
          }
  
          protected boolean applyNull() {
              return not;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue instanceof Byte
                      || fieldValue instanceof Short
                      || fieldValue instanceof Integer
                      || fieldValue instanceof Long
              ) {
                  long fieldValueLong = ((Number) fieldValue).longValue();
  
                  if (fieldValueLong >= begin && fieldValueLong <= end) {
                      return !not;
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof Float
                      || fieldValue instanceof Double
              ) {
                  double fieldValueDouble = ((Number) fieldValue).doubleValue();
  
                  if (fieldValueDouble >= begin && fieldValueDouble <= end) {
                      return !not;
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof BigDecimal) {
                  BigDecimal decimal = (BigDecimal) fieldValue;
                  int cmpBegin = decimal.compareTo(BigDecimal.valueOf(begin));
                  int cmpEnd = decimal.compareTo(BigDecimal.valueOf(end));
  
                  if (cmpBegin >= 0 && cmpEnd <= 0) {
                      return !not;
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof BigInteger) {
                  BigInteger bigInt = (BigInteger) fieldValue;
                  int cmpBegin = bigInt.compareTo(BigInteger.valueOf(begin));
                  int cmpEnd = bigInt.compareTo(BigInteger.valueOf(end));
  
                  if (cmpBegin >= 0 && cmpEnd <= 0) {
                      return !not;
                  }
  
                  return not;
              }
  
              return not;
          }
      }
  
      static final class NameLongContainsSegment
              extends NameFilter {
          private final long[] values;
          private final boolean not;
  
          public NameLongContainsSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  long[] values, boolean not) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, null);
              this.values = values;
              this.not = not;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue instanceof Collection) {
                  Collection collection = (Collection) fieldValue;
  
                  boolean containsAll = true;
                  for (long value : values) {
                      boolean containsItem = false;
                      for (Object item : collection) {
                          long longItem;
                          if (item instanceof Byte
                                  || item instanceof Short
                                  || item instanceof Integer
                                  || item instanceof Long) {
                              longItem = ((Number) item).longValue();
                              if (longItem == value) {
                                  containsItem = true;
                                  break;
                              }
                          }
  
                          if (item instanceof Float) {
                              if (value == (Float) item) {
                                  containsItem = true;
                                  break;
                              }
                          }
  
                          if (item instanceof Double) {
                              if (value == (Double) item) {
                                  containsItem = true;
                                  break;
                              }
                          }
  
                          if (item instanceof BigDecimal) {
                              BigDecimal decimal = (BigDecimal) item;
                              long longValue = decimal.longValue();
                              if (value == longValue && decimal.compareTo(BigDecimal.valueOf(value)) == 0) {
                                  containsItem = true;
                                  break;
                              }
                          }
  
                          if (item instanceof BigInteger) {
                              BigInteger bigiInt = (BigInteger) item;
                              long longValue = bigiInt.longValue();
                              if (value == longValue && bigiInt.equals(BigInteger.valueOf(value))) {
                                  containsItem = true;
                                  break;
                              }
                          }
                      }
  
                      if (!containsItem) {
                          containsAll = false;
                          break;
                      }
                  }
  
                  if (containsAll) {
                      return !not;
                  }
              }
  
              return not;
          }
      }
  
      static final class NameIntInSegment
              extends NameFilter {
          private final long[] values;
          private final boolean not;
  
          public NameIntInSegment(
                  String fieldName,
                  long fieldNameNameHash,
                  String[] fieldName2,
                  long[] fieldNameNameHash2,
                  Function expr,
                  long[] values,
                  boolean not) {
              super(fieldName, fieldNameNameHash, fieldName2, fieldNameNameHash2, expr);
              this.values = values;
              this.not = not;
          }
  
          protected boolean applyNull() {
              return not;
          }
  
          @Override
          public boolean apply(Object fieldValue) {
              if (fieldValue instanceof Byte
                      || fieldValue instanceof Short
                      || fieldValue instanceof Integer
                      || fieldValue instanceof Long
              ) {
                  long fieldValueLong = ((Number) fieldValue).longValue();
  
                  for (long value : values) {
                      if (value == fieldValueLong) {
                          return !not;
                      }
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof Float
                      || fieldValue instanceof Double
              ) {
                  double fieldValueDouble = ((Number) fieldValue).doubleValue();
  
                  for (long value : values) {
                      if (value == fieldValueDouble) {
                          return !not;
                      }
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof BigDecimal) {
                  BigDecimal decimal = (BigDecimal) fieldValue;
                  long longValue = decimal.longValue();
                  for (long value : values) {
                      if (value == longValue && decimal.compareTo(BigDecimal.valueOf(value)) == 0) {
                          return !not;
                      }
                  }
  
                  return not;
              }
  
              if (fieldValue instanceof BigInteger) {
                  BigInteger bigiInt = (BigInteger) fieldValue;
                  long longValue = bigiInt.longValue();
                  for (long value : values) {
                      if (value == longValue && bigiInt.equals(BigInteger.valueOf(value))) {
                          return !not;
                      }
                  }
  
                  return not;
              }
  
              return not;
          }
      }
  
      static final class NameName
              extends NameFilter {
          final String fieldName1;
          final long fieldNameName1Hash;
  
          public NameName(
                  String fieldName,
                  long fieldNameNameHash,
                  String fieldName1,
                  long fieldNameName1Hash
          ) {
              super(fieldName, fieldNameNameHash);
              this.fieldName1 = fieldName1;
              this.fieldNameName1Hash = fieldNameName1Hash;
          }
  
          @Override
          public boolean apply(JSONPath.Context context, Object object) {
              if (object == null) {
                  return false;
              }
  
              JSONWriter.Context writerContext = context.path.getWriterContext();
  
              Object fieldValue, fieldValue1;
              if (object instanceof Map) {
                  Map map = (Map) object;
                  fieldValue = map.get(fieldName);
                  fieldValue1 = map.get(fieldName1);
              } else {
                  ObjectWriter objectWriter = writerContext.getObjectWriter(object.getClass());
                  if (objectWriter instanceof ObjectWriterAdapter) {
                      FieldWriter fieldWriter = objectWriter.getFieldWriter(fieldNameNameHash);
                      if (fieldWriter == null) {
                          return false;
                      }
                      fieldValue = fieldWriter.getFieldValue(object);
  
                      FieldWriter fieldWriter1 = objectWriter.getFieldWriter(fieldNameNameHash);
                      if (fieldWriter1 == null) {
                          return false;
                      }
                      fieldValue1 = fieldWriter1.getFieldValue(object);
                  } else {
                      return false;
                  }
              }
  
              return Objects.equals(fieldValue, fieldValue1);
          }
  
          @Override
          boolean apply(Object fieldValue) {
              throw new JSONException("TODO");
          }
      }
  
      static final class SegmentFilter
              extends JSONPathFilter {
          final JSONPathSegment expr;
          final Operator operator;
          final Object value;
  
          public SegmentFilter(JSONPathSegment expr, Operator operator, Object value) {
              this.expr = expr;
              this.operator = operator;
              this.value = value;
          }
  
          @Override
          boolean apply(JSONPath.Context context, Object object) {
              if (object == null) {
                  return false;
              }
              JSONPath.Context context0 = new JSONPath.Context(null, null, expr, null, 0);
              context0.root = object;
              expr.eval(context0);
              Object result = context0.value;
              int cmp = TypeUtils.compare(result, value);
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              throw new JSONException("UnsupportedOperation " + getClass());
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  JSONArray array = new JSONArray(list.size());
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              throw new JSONException("UnsupportedOperation " + object.getClass());
          }
  
          @Override
          public boolean remove(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = list.size() - 1; i >= 0; i--) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          list.remove(i);
                      }
                  }
                  return true;
              }
  
              throw new JSONException("UnsupportedOperation " + getClass());
          }
  
          @Override
          public void set(JSONPath.Context context, Object value) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          list.set(i, value);
                      }
                  }
                  return;
              }
  
              throw new JSONException("UnsupportedOperation ");
          }
      }
  
      static final class RangeIndexSegmentFilter
              extends JSONPathFilter {
          final RangeIndexSegment expr;
          final Operator operator;
          final Object value;
  
          public RangeIndexSegmentFilter(RangeIndexSegment expr, Operator operator, Object value) {
              this.expr = expr;
              this.operator = operator;
              this.value = value;
          }
  
          @Override
          boolean apply(JSONPath.Context context, Object object) {
              if (object == null) {
                  return false;
              }
              JSONPath.Context context0 = new JSONPath.Context(null, null, expr, null, 0);
              context0.root = object;
              expr.eval(context0);
              List result = (List) context0.value;
              for (int i = 0; i < result.size(); i++) {
                  Object item = result.get(i);
                  int cmp = TypeUtils.compare(item, value);
                  boolean itemResult;
                  switch (operator) {
                      case LT:
                          itemResult = cmp < 0;
                          break;
                      case LE:
                          itemResult = cmp <= 0;
                          break;
                      case EQ:
                          itemResult = cmp == 0;
                          break;
                      case NE:
                          itemResult = cmp != 0;
                          break;
                      case GT:
                          itemResult = cmp > 0;
                          break;
                      case GE:
                          itemResult = cmp >= 0;
                          break;
                      default:
                          throw new UnsupportedOperationException();
                  }
                  if (!itemResult) {
                      return false;
                  }
              }
              return true;
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              throw new JSONException("UnsupportedOperation " + getClass());
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  JSONArray array = new JSONArray(list.size());
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              throw new JSONException("UnsupportedOperation " + object.getClass());
          }
      }
  
      static final class Segment2Filter
              extends JSONPathFilter {
          final JSONPathSegment left;
          final Operator operator;
          final JSONPathSegment right;
  
          public Segment2Filter(JSONPathSegment left, Operator operator, JSONPathSegment right) {
              this.left = left;
              this.operator = operator;
              this.right = right;
          }
  
          @Override
          boolean apply(JSONPath.Context context, Object object) {
              if (object == null) {
                  return false;
              }
  
              JSONPath.Context context0 = new JSONPath.Context(null, null, left, null, 0);
              context0.root = object;
              left.eval(context0);
              Object result0 = context0.value;
  
              JSONPath.Context context1 = new JSONPath.Context(null, null, right, null, 0);
              context1.root = object;
              right.eval(context1);
              Object result1 = context1.value;
  
              int cmp = TypeUtils.compare(result0, result1);
              switch (operator) {
                  case LT:
                      return cmp < 0;
                  case LE:
                      return cmp <= 0;
                  case EQ:
                      return cmp == 0;
                  case NE:
                      return cmp != 0;
                  case GT:
                      return cmp > 0;
                  case GE:
                      return cmp >= 0;
                  default:
                      throw new UnsupportedOperationException();
              }
          }
  
          @Override
          public void accept(JSONReader jsonReader, JSONPath.Context context) {
              throw new JSONException("UnsupportedOperation " + getClass());
          }
  
          @Override
          public void eval(JSONPath.Context context) {
              Object object = context.parent == null
                      ? context.root
                      : context.parent.value;
  
              if (object instanceof List) {
                  List list = (List) object;
                  JSONArray array = new JSONArray(list.size());
                  for (int i = 0; i < list.size(); i++) {
                      Object item = list.get(i);
                      if (apply(context, item)) {
                          array.add(item);
                      }
                  }
                  context.value = array;
                  context.eval = true;
                  return;
              }
  
              throw new JSONException("UnsupportedOperation " + object.getClass());
          }
      }
  }
  