content: package com.alibaba.fastjson;
  
  import com.alibaba.fastjson.parser.DefaultJSONParser;
  import com.alibaba.fastjson.parser.Feature;
  import com.alibaba.fastjson.parser.JSONLexer;
  import com.alibaba.fastjson.parser.JSONLexerBase;
  import com.alibaba.fastjson.parser.JSONToken;
  import com.alibaba.fastjson.parser.ParserConfig;
  import com.alibaba.fastjson.parser.deserializer.FieldDeserializer;
  import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;
  import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
  import com.alibaba.fastjson.serializer.FieldSerializer;
  import com.alibaba.fastjson.serializer.JavaBeanSerializer;
  import com.alibaba.fastjson.serializer.ObjectSerializer;
  import com.alibaba.fastjson.serializer.SerializeConfig;
  import com.alibaba.fastjson.util.IOUtils;
  import com.alibaba.fastjson.util.TypeUtils;
  
  import java.lang.reflect.Array;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Type;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.math.RoundingMode;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Calendar;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.IdentityHashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.UUID;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentMap;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  /**
   * @author wenshao[szujobs@hotmail.com]
   * @since 1.2.0
   */
  public class JSONPath implements JSONAware {
      private static ConcurrentMap<String, JSONPath> pathCache  = new ConcurrentHashMap<String, JSONPath>(128, 0.75f, 1);
  
      private final String                           path;
      private Segment[]                              segments;
      private boolean                                hasRefSegment;
  
      private SerializeConfig                        serializeConfig;
      private ParserConfig                           parserConfig;
  
      private boolean                                ignoreNullValue;
  
      public JSONPath(String path){
          this(path, SerializeConfig.getGlobalInstance(), ParserConfig.getGlobalInstance(), true);
      }
  
      public JSONPath(String path, boolean ignoreNullValue){
          this(path, SerializeConfig.getGlobalInstance(), ParserConfig.getGlobalInstance(), ignoreNullValue);
      }
  
      public JSONPath(String path, SerializeConfig serializeConfig, ParserConfig parserConfig, boolean ignoreNullValue){
          if (path == null || path.length() == 0) {
              throw new JSONPathException("json-path can not be null or empty");
          }
  
          this.path = path;
          this.serializeConfig = serializeConfig;
          this.parserConfig = parserConfig;
          this.ignoreNullValue = ignoreNullValue;
      }
  
      protected void init() {
          if (segments != null) {
              return;
          }
  
          if ("*".equals(path)) {
              this.segments = new Segment[] { WildCardSegment.instance };
          } else {
              JSONPathParser parser = new JSONPathParser(path);
              this.segments = parser.explain();
              this.hasRefSegment = parser.hasRefSegment;
          }
      }
  
      public boolean isRef() {
          try {
              init();
              for (int i = 0; i < segments.length; ++i) {
                  Segment segment = segments[i];
                  Class segmentType = segment.getClass();
                  if (segmentType == ArrayAccessSegment.class
                          || segmentType == PropertySegment.class) {
                      continue;
                  }
                  return false;
              }
              return true;
          } catch (JSONPathException ex) {
              // skip
              return false;
          }
      }
  
      public Object eval(Object rootObject) {
          if (rootObject == null) {
              return null;
          }
  
          init();
  
          Object currentObject = rootObject;
          for (int i = 0; i < segments.length; ++i) {
              Segment segment = segments[i];
              currentObject = segment.eval(this, rootObject, currentObject);
          }
          return currentObject;
      }
      
      /**
       * @since 1.2.76
       * @param rootObject
       * @param clazz
       * @param parserConfig
       * @return
       */
      public <T> T eval(Object rootObject, Type clazz, ParserConfig parserConfig) {
          Object obj = this.eval(rootObject);
          return TypeUtils.cast(obj, clazz, parserConfig);
      }
      
      /**
       * @since 1.2.76
       * @param rootObject
       * @param clazz
       * @return
       */
      public <T> T eval(Object rootObject, Type clazz) {
          return this.eval(rootObject, clazz, ParserConfig.getGlobalInstance());
      }
  
      public Object extract(DefaultJSONParser parser) {
          if (parser == null) {
              return null;
          }
  
          init();
  
          if (hasRefSegment) {
              Object root = parser.parse();
              return this.eval(root);
          }
  
          if (segments.length == 0) {
              return parser.parse();
          }
  
          Segment lastSegment = segments[segments.length - 1];
          if (lastSegment instanceof TypeSegment
                  || lastSegment instanceof FloorSegment
                  || lastSegment instanceof MultiIndexSegment) {
              return eval(
                      parser.parse());
          }
  
          Context context = null;
          for (int i = 0; i < segments.length; ++i) {
              Segment segment = segments[i];
              boolean last = i == segments.length - 1;
  
              if (context != null && context.object != null) {
                  context.object = segment.eval(this, null, context.object);
                  continue;
              }
  
              boolean eval;
  
              if (!last) {
                  Segment nextSegment = segments[i + 1];
                  if (segment instanceof PropertySegment
                          && ((PropertySegment) segment).deep
                          && (nextSegment instanceof ArrayAccessSegment
                              || nextSegment instanceof MultiIndexSegment
                              || nextSegment instanceof MultiPropertySegment
                              || nextSegment instanceof SizeSegment
                              || nextSegment instanceof PropertySegment
                              || nextSegment instanceof FilterSegment))
                  {
                      eval = true;
                  } else if (nextSegment instanceof ArrayAccessSegment
                          && ((ArrayAccessSegment) nextSegment).index < 0) {
                      eval = true;
                  } else if (nextSegment instanceof FilterSegment) {
                      eval = true;
                  } else if (segment instanceof WildCardSegment) {
                      eval = true;
                  }else if(segment instanceof MultiIndexSegment){
                      eval = true;
                  } else {
                      eval = false;
                  }
              } else {
                  eval = true;
              }
  
              context = new Context(context, eval);
              segment.extract(this, parser, context);
          }
  
          return context.object;
      }
  
      private static class Context {
          final Context parent;
          final boolean eval;
          Object object;
  
          public Context(Context parent, boolean eval) {
              this.parent = parent;
              this.eval = eval;
          }
      }
  
      public boolean contains(Object rootObject) {
          if (rootObject == null) {
              return false;
          }
  
          init();
  
          Object currentObject = rootObject;
          for (int i = 0; i < segments.length; ++i) {
              Object parentObject = currentObject;
              currentObject = segments[i].eval(this, rootObject, currentObject);
              if (currentObject == null) {
                  return false;
              }
  
              if (currentObject == Collections.EMPTY_LIST && parentObject instanceof List) {
                  return ((List) parentObject).contains(currentObject);
              }
          }
  
          return true;
      }
  
      @SuppressWarnings("rawtypes")
      public boolean containsValue(Object rootObject, Object value) {
          Object currentObject = eval(rootObject);
  
          if (currentObject == value) {
              return true;
          }
  
          if (currentObject == null) {
              return false;
          }
  
          if (currentObject instanceof Iterable) {
              Iterator it = ((Iterable) currentObject).iterator();
              while (it.hasNext()) {
                  Object item = it.next();
                  if (eq(item, value)) {
                      return true;
                  }
              }
  
              return false;
          }
  
          return eq(currentObject, value);
      }
  
      public int size(Object rootObject) {
          if (rootObject == null) {
              return -1;
          }
  
          init();
  
          Object currentObject = rootObject;
          for (int i = 0; i < segments.length; ++i) {
              currentObject = segments[i].eval(this, rootObject, currentObject);
          }
  
          return evalSize(currentObject);
      }
  
      /**
       * Extract keySet or field names from rootObject on this JSONPath.
       * 
       * @param rootObject Can be a map or custom object. Array and Collection are not supported.
       * @return Set of keys, or <code>null</code> if not supported.
       */
      public Set<?> keySet(Object rootObject) {
          if (rootObject == null) {
              return null;
          }
  
          init();
  
          Object currentObject = rootObject;
          for (int i = 0; i < segments.length; ++i) {
              currentObject = segments[i].eval(this, rootObject, currentObject);
          }
  
          return evalKeySet(currentObject);
      }
  
      public void patchAdd(Object rootObject, Object value, boolean replace) {
          if (rootObject == null) {
              return;
          }
  
          init();
  
          Object currentObject = rootObject;
          Object parentObject = null;
          for (int i = 0; i < segments.length; ++i) {
              parentObject = currentObject;
              Segment segment = segments[i];
              currentObject = segment.eval(this, rootObject, currentObject);
              if (currentObject == null && i != segments.length - 1) {
                  if (segment instanceof PropertySegment) {
                      currentObject = new JSONObject();
                      ((PropertySegment) segment).setValue(this, parentObject, currentObject);
                  }
              }
          }
  
          Object result = currentObject;
  
          if ((!replace) && result instanceof Collection) {
              Collection collection = (Collection) result;
              collection.add(value);
              return;
          }
  
          Object newResult;
  
          if (result != null && !replace) {
              Class<?> resultClass = result.getClass();
  
              if (resultClass.isArray()) {
                  int length = Array.getLength(result);
                  Object descArray = Array.newInstance(resultClass.getComponentType(), length + 1);
  
                  System.arraycopy(result, 0, descArray, 0, length);
                  Array.set(descArray, length, value);
                  newResult = descArray;
              }
              else if (Map.class.isAssignableFrom(resultClass)) {
                  newResult = value;
              } else {
                  throw new JSONException("unsupported array put operation. " + resultClass);
              }
          } else {
              newResult = value;
          }
  
          Segment lastSegment = segments[segments.length - 1];
          if (lastSegment instanceof PropertySegment) {
              PropertySegment propertySegment = (PropertySegment) lastSegment;
              propertySegment.setValue(this, parentObject, newResult);
              return;
          }
  
          if (lastSegment instanceof ArrayAccessSegment) {
              ((ArrayAccessSegment) lastSegment).setValue(this, parentObject, newResult);
              return;
          }
  
          throw new UnsupportedOperationException();
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      public void arrayAdd(Object rootObject, Object... values) {
          if (values == null || values.length == 0) {
              return;
          }
  
          if (rootObject == null) {
              return;
          }
  
          init();
  
          Object currentObject = rootObject;
          Object parentObject = null;
          for (int i = 0; i < segments.length; ++i) {
              if (i == segments.length - 1) {
                  parentObject = currentObject;
              }
              currentObject = segments[i].eval(this, rootObject, currentObject);
          }
  
          Object result = currentObject;
  
          if (result == null) {
              throw new JSONPathException("value not found in path " + path);
          }
  
          if (result instanceof Collection) {
              Collection collection = (Collection) result;
              for (Object value : values) {
                  collection.add(value);
              }
              return;
          }
  
          Class<?> resultClass = result.getClass();
  
          Object newResult;
          if (resultClass.isArray()) {
              int length = Array.getLength(result);
              Object descArray = Array.newInstance(resultClass.getComponentType(), length + values.length);
  
              System.arraycopy(result, 0, descArray, 0, length);
              for (int i = 0; i < values.length; ++i) {
                  Array.set(descArray, length + i, values[i]);
  
              }
              newResult = descArray;
          } else {
              throw new JSONException("unsupported array put operation. " + resultClass);
          }
  
          Segment lastSegment = segments[segments.length - 1];
          if (lastSegment instanceof PropertySegment) {
              PropertySegment propertySegment = (PropertySegment) lastSegment;
              propertySegment.setValue(this, parentObject, newResult);
              return;
          }
  
          if (lastSegment instanceof ArrayAccessSegment) {
              ((ArrayAccessSegment) lastSegment).setValue(this, parentObject, newResult);
              return;
          }
  
          throw new UnsupportedOperationException();
      }
      
      public boolean remove(Object rootObject) {
          if (rootObject == null) {
              return false;
          }
  
          init();
  
          Object currentObject = rootObject;
          Object parentObject = null;
  
          Segment lastSegment = segments[segments.length - 1];
          for (int i = 0; i < segments.length; ++i) {
              if (i == segments.length - 1) {
                  parentObject = currentObject;
                  break;
              }
              Segment segement = segments[i];
              if (i == segments.length - 2
                      && lastSegment instanceof FilterSegment
                      && segement instanceof PropertySegment
              ) {
                  FilterSegment filterSegment = (FilterSegment) lastSegment;
  
                  if (currentObject instanceof List) {
                      PropertySegment propertySegment = (PropertySegment) segement;
                      List list = (List) currentObject;
  
                      for (Iterator it = list.iterator();it.hasNext();) {
                          Object item = it.next();
                          Object result = propertySegment.eval(this, rootObject, item);
                          if (result instanceof Iterable) {
                              filterSegment.remove(this, rootObject, result);
                          } else if (result instanceof Map) {
                              if (filterSegment.filter.apply(this, rootObject, currentObject, result)) {
                                  it.remove();
                              }
                          }
                      }
                      return true;
                  } else if (currentObject instanceof Map) {
                      PropertySegment propertySegment = (PropertySegment) segement;
                      Object result = propertySegment.eval(this, rootObject, currentObject);
                      if (result == null) {
                          return false;
                      }
                      if (result instanceof Map
                              && filterSegment.filter.apply(this, rootObject, currentObject, result)) {
                          propertySegment.remove(this, currentObject);
                          return true;
                      }
                  }
              }
  
              currentObject = segement.eval(this, rootObject, currentObject);
              if (currentObject == null) {
                  break;
              }
          }
  
          if (parentObject == null) {
              return false;
          }
  
  
          if (lastSegment instanceof PropertySegment) {
              PropertySegment propertySegment = (PropertySegment) lastSegment;
  
              if (parentObject instanceof Collection) {
                  if (segments.length > 1) {
                      Segment parentSegment = segments[segments.length - 2];
                      if (parentSegment instanceof RangeSegment || parentSegment instanceof MultiIndexSegment) {
                          Collection collection = (Collection) parentObject;
                          boolean removedOnce = false;
                          for (Object item : collection) {
                              boolean removed = propertySegment.remove(this, item);
                              if (removed) {
                                  removedOnce = true;
                              }
                          }
                          return removedOnce;
                      }
                  }
              }
              return propertySegment.remove(this, parentObject);
          }
  
          if (lastSegment instanceof ArrayAccessSegment) {
              return ((ArrayAccessSegment) lastSegment).remove(this, parentObject);
          }
  
          if (lastSegment instanceof FilterSegment) {
              FilterSegment filterSegment = (FilterSegment) lastSegment;
              return filterSegment.remove(this, rootObject, parentObject);
          }
  
          throw new UnsupportedOperationException();
      }
  
      public boolean set(Object rootObject, Object value) {
          return set(rootObject, value, true);
      }
  
      public boolean set(Object rootObject, Object value, boolean p) {
          if (rootObject == null) {
              return false;
          }
  
          init();
  
          Object currentObject = rootObject;
          Object parentObject = null;
          for (int i = 0; i < segments.length; ++i) {
  //            if (i == segments.length - 1) {
  //                parentObject = currentObject;
  //                break;
  //            }
  //            
              parentObject = currentObject;
              Segment segment = segments[i];
              currentObject = segment.eval(this, rootObject, currentObject);
              if (currentObject == null) {
                  Segment nextSegment = null;
                  if (i < segments.length - 1) {
                      nextSegment = segments[i + 1];
                  }
  
                  Object newObj = null;
                  if (nextSegment instanceof PropertySegment) {
                      JavaBeanDeserializer beanDeserializer = null;
                      Class<?> fieldClass = null;
                      if (segment instanceof PropertySegment) {
                          String propertyName = ((PropertySegment) segment).propertyName;
                          Class<?> parentClass = parentObject.getClass();
                          JavaBeanDeserializer parentBeanDeserializer = getJavaBeanDeserializer(parentClass);
                          if (parentBeanDeserializer != null) {
                              FieldDeserializer fieldDeserializer = parentBeanDeserializer.getFieldDeserializer(propertyName);
                              fieldClass = fieldDeserializer.fieldInfo.fieldClass;
                              beanDeserializer = getJavaBeanDeserializer(fieldClass);
                          }
                      }
  
                      if (beanDeserializer != null) {
  
                          if (beanDeserializer.beanInfo.defaultConstructor != null) {
                              newObj = beanDeserializer.createInstance(null, fieldClass);
                          } else {
                              return false;
                          }
                      } else {
                          newObj = new JSONObject();
                      }
                  } else if (nextSegment instanceof ArrayAccessSegment) {
                      newObj = new JSONArray();
                  }
                  
                  if (newObj != null) {
                      if (segment instanceof PropertySegment) {
                          PropertySegment propSegement = (PropertySegment) segment;
                          propSegement.setValue(this, parentObject, newObj);
                          currentObject = newObj;
                          continue;
                      } else if (segment instanceof ArrayAccessSegment) {
                          ArrayAccessSegment arrayAccessSegement = (ArrayAccessSegment) segment;
                          arrayAccessSegement.setValue(this, parentObject, newObj);
                          currentObject = newObj;
                          continue;
                      }
                  }
                  
                  break;
              }
          }
  
          if (parentObject == null) {
              return false;
          }
  
          Segment lastSegment = segments[segments.length - 1];
          if (lastSegment instanceof PropertySegment) {
              PropertySegment propertySegment = (PropertySegment) lastSegment;
              propertySegment.setValue(this, parentObject, value);
              return true;
          }
  
          if (lastSegment instanceof ArrayAccessSegment) {
              return ((ArrayAccessSegment) lastSegment).setValue(this, parentObject, value);
          }
  
          throw new UnsupportedOperationException();
      }
  
      public static Object eval(Object rootObject, String path) {
          JSONPath jsonpath = compile(path);
          return jsonpath.eval(rootObject);
      }
  
      public static Object eval(Object rootObject, String path, boolean ignoreNullValue) {
          JSONPath jsonpath = compile(path, ignoreNullValue);
          return jsonpath.eval(rootObject);
      }
  
      public static int size(Object rootObject, String path) {
          JSONPath jsonpath = compile(path);
          Object result = jsonpath.eval(rootObject);
          return jsonpath.evalSize(result);
      }
  
      /**
       * Compile jsonPath and use it to extract keySet or field names from rootObject.
       * 
       * @param rootObject Can be a map or custom object. Array and Collection are not supported.
       * @param path JSONPath string to be compiled.
       * @return Set of keys, or <code>null</code> if not supported.
       */
      public static Set<?> keySet(Object rootObject, String path) {
          JSONPath jsonpath = compile(path);
          Object result = jsonpath.eval(rootObject);
          return jsonpath.evalKeySet(result);
      }
  
      public static boolean contains(Object rootObject, String path) {
          if (rootObject == null) {
              return false;
          }
  
          JSONPath jsonpath = compile(path);
          return jsonpath.contains(rootObject);
      }
  
      public static boolean containsValue(Object rootObject, String path, Object value) {
          JSONPath jsonpath = compile(path);
          return jsonpath.containsValue(rootObject, value);
      }
  
      public static void arrayAdd(Object rootObject, String path, Object... values) {
          JSONPath jsonpath = compile(path);
          jsonpath.arrayAdd(rootObject, values);
      }
  
      public static boolean set(Object rootObject, String path, Object value) {
          JSONPath jsonpath = compile(path);
          return jsonpath.set(rootObject, value);
      }
      
      public static boolean remove(Object root, String path) {
          JSONPath jsonpath = compile(path);
          return jsonpath.remove(root);
      }
  
      public static JSONPath compile(String path) {
          if (path == null) {
              throw new JSONPathException("jsonpath can not be null");
          }
          
          JSONPath jsonpath = pathCache.get(path);
          if (jsonpath == null) {
              jsonpath = new JSONPath(path);
              if (pathCache.size() < 1024) {
                  pathCache.putIfAbsent(path, jsonpath);
                  jsonpath = pathCache.get(path);
              }
          }
          return jsonpath;
      }
  
      public static JSONPath compile(String path, boolean ignoreNullValue) {
          if (path == null) {
              throw new JSONPathException("jsonpath can not be null");
          }
  
          JSONPath jsonpath = pathCache.get(path);
          if (jsonpath == null) {
              jsonpath = new JSONPath(path, ignoreNullValue);
              if (pathCache.size() < 1024) {
                  pathCache.putIfAbsent(path, jsonpath);
                  jsonpath = pathCache.get(path);
              }
          }
          return jsonpath;
      }
  
      /**
       * @since 1.2.9
       * @param json
       * @param path
       * @return
       */
      public static Object read(String json, String path) {
          return compile(path)
                  .eval(
                          JSON.parse(json)
                  );
      }
      
      /**
       * @since 1.2.76
       * @param json
       * @param path
       * @param clazz
       * @param parserConfig
       * @return
       */
      public static <T> T read(String json, String path, Type clazz, ParserConfig parserConfig) {
          return compile(path).eval(JSON.parse(json), clazz, parserConfig);
      }
      
      /**
       * @since 1.2.76
       * @param json
       * @param path
       * @param clazz
       * @return
       */
      public static <T> T read(String json, String path, Type clazz) {
          return read(json, path, clazz, null);
      }
  
      /**
       * @since 1.2.51
       * @param json
       * @param path
       * @return
       */
      public static Object extract(String json, String path, ParserConfig config, int features, Feature... optionFeatures) {
          features |= Feature.OrderedField.mask;
          DefaultJSONParser parser = new DefaultJSONParser(json, config, features);
          JSONPath jsonPath = compile(path);
          Object result = jsonPath.extract(parser);
          parser.lexer.close();
          return result;
      }
  
      public static Object extract(String json, String path) {
          return extract(json, path, ParserConfig.global, JSON.DEFAULT_PARSER_FEATURE);
      }
  
      public static Map<String, Object> paths(Object javaObject) {
          return paths(javaObject, SerializeConfig.globalInstance);
      }
      
      public static Map<String, Object> paths(Object javaObject, SerializeConfig config) {
          Map<Object, String> values = new IdentityHashMap<Object, String>();
          Map<String, Object> paths = new HashMap<String, Object>();
  
          paths(values, paths, "/", javaObject, config);
          return paths;
      }
  
      private static void paths(Map<Object, String> values, Map<String, Object> paths, String parent, Object javaObject, SerializeConfig config) {
          if (javaObject == null) {
              return;
          }
  
          String p = values.put(javaObject, parent);
          if (p != null) {
              Class<?> type = javaObject.getClass();
              boolean basicType =  type == String.class
                      || type == Boolean.class
                      || type == Character.class
                      || type == UUID.class
                      || type.isEnum()
                      || javaObject instanceof Number
                      || javaObject instanceof Date
                      ;
  
              if (!basicType) {
                  return;
              }
          }
  
          paths.put(parent, javaObject);
  
          if (javaObject instanceof Map) {
              Map map = (Map) javaObject;
  
              for (Object entryObj : map.entrySet()) {
                  Map.Entry entry = (Map.Entry) entryObj;
                  Object key = entry.getKey();
  
                  if (key instanceof String) {
                      String path = parent.equals("/") ?  "/" + key : parent + "/" + key;
                      paths(values, paths, path, entry.getValue(), config);
                  }
              }
              return;
          }
  
          if (javaObject instanceof Collection) {
              Collection collection = (Collection) javaObject;
  
              int i = 0;
              for (Object item : collection) {
                  String path = parent.equals("/") ?  "/" + i : parent + "/" + i;
                  paths(values, paths, path, item, config);
                  ++i;
              }
  
              return;
          }
  
          Class<?> clazz = javaObject.getClass();
  
          if (clazz.isArray()) {
              int len = Array.getLength(javaObject);
  
              for (int i = 0; i < len; ++i) {
                  Object item = Array.get(javaObject, i);
  
                  String path = parent.equals("/") ?  "/" + i : parent + "/" + i;
                  paths(values, paths, path, item, config);
              }
  
              return;
          }
  
          if (ParserConfig.isPrimitive2(clazz) || clazz.isEnum()) {
              return;
          }
  
          ObjectSerializer serializer = config.getObjectWriter(clazz);
          if (serializer instanceof JavaBeanSerializer) {
              JavaBeanSerializer javaBeanSerializer = (JavaBeanSerializer) serializer;
  
              try {
                  Map<String, Object> fieldValues = javaBeanSerializer.getFieldValuesMap(javaObject);
                  for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
                      String key = entry.getKey();
  
                      if (key instanceof String) {
                          String path = parent.equals("/") ?  "/" + key : parent + "/" + key;
                          paths(values, paths, path, entry.getValue(), config);
                      }
                  }
              } catch (Exception e) {
                  throw new JSONException("toJSON error", e);
              }
              return;
          }
  
          return;
      }
  
      public String getPath() {
          return path;
      }
  
      static class JSONPathParser {
  
          private final String path;
          private int          pos;
          private char         ch;
          private int          level;
          private boolean      hasRefSegment;
          private static final String strArrayRegex = "\'\\s*,\\s*\'";
          private static final Pattern strArrayPatternx = Pattern.compile(strArrayRegex);
  
          public JSONPathParser(String path){
              this.path = path;
              next();
          }
  
          void next() {
              ch = path.charAt(pos++);
          }
  
          char getNextChar() {
              return path.charAt(pos);
          }
  
          boolean isEOF() {
              return pos >= path.length();
          }
  
          Segment readSegement() {
              if (level == 0 && path.length() == 1) {
                  if (isDigitFirst(ch)) {
                      int index = ch - '0';
                      return new ArrayAccessSegment(index);
                  } else if ((ch >= 'a' && ch <= 'z') || ((ch >= 'A' && ch <= 'Z'))) {
                      return new PropertySegment(Character.toString(ch), false);
                  }
              }
              while (!isEOF()) {
                  skipWhitespace();
  
                  if (ch == '$') {
                      next();
                      skipWhitespace();
                      if (ch == '?') {
                          return new FilterSegment(
                                  (Filter) parseArrayAccessFilter(false));
                      }
                      continue;
                  }
  
                  if (ch == '.' || ch == '/') {
                      int c0 = ch;
                      boolean deep = false;
                      next();
                      if (c0 == '.' && ch == '.') {
                          next();
                          deep = true;
                          if (path.length() > pos + 3
                                  && ch == '['
                                  && path.charAt(pos) == '*'
                                  && path.charAt(pos + 1) == ']'
                                  && path.charAt(pos + 2) == '.') {
                              next();
                              next();
                              next();
                              next();
                          }
                      }
                      if (ch == '*' || (deep && ch == '[')) {
                          boolean objectOnly = ch == '[';
                          if (!isEOF()) {
                              next();
                          }
  
                          if (deep) {
                              if (objectOnly) {
                                  return WildCardSegment.instance_deep_objectOnly;
                              } else {
                                  return WildCardSegment.instance_deep;
                              }
                          } else {
                              return WildCardSegment.instance;
                          }
                      }
                      
                      if (isDigitFirst(ch)) {
                          return parseArrayAccess(false);
                      }
  
                      String propertyName = readName();
                      if (ch == '(') {
                          next();
                          if (ch == ')') {
                              if (!isEOF()) {
                                  next();
                              }
  
                              if ("size".equals(propertyName) || "length".equals(propertyName)) {
                                  return SizeSegment.instance;
                              } else if ("max".equals(propertyName)) {
                                  return MaxSegment.instance;
                              } else if ("min".equals(propertyName)) {
                                  return MinSegment.instance;
                              } else if ("keySet".equals(propertyName)) {
                                  return KeySetSegment.instance;
                              } else if ("type".equals(propertyName)) {
                                  return TypeSegment.instance;
                              } else if ("floor".equals(propertyName)) {
                                  return FloorSegment.instance;
                              }
  
                              throw new JSONPathException("not support jsonpath : " + path);
                          }
  
                          throw new JSONPathException("not support jsonpath : " + path);
                      }
  
                      return new PropertySegment(propertyName, deep);
                  }
  
                  if (ch == '[') {
                      return parseArrayAccess(true);
                  }
  
                  if (level == 0) {
                      String propertyName = readName();
  
                      return new PropertySegment(propertyName, false);
                  }
  
                  if (ch == '?') {
                      return new FilterSegment(
                              (Filter) parseArrayAccessFilter(false));
                  }
  
                  throw new JSONPathException("not support jsonpath : " + path);
              }
  
              return null;
          }
  
          public final void skipWhitespace() {
              for (;;) {
                  if (ch <= ' ' && (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t' || ch == '\f' || ch == '\b')) {
                      next();
                      continue;
                  } else {
                      break;
                  }
              }
          }
  
          Segment parseArrayAccess(boolean acceptBracket) {
              Object object = parseArrayAccessFilter(acceptBracket);
              if (object instanceof Segment) {
                  return ((Segment) object);
              }
              return new FilterSegment((Filter) object);
          }
  
          Object parseArrayAccessFilter(boolean acceptBracket) {
              if (acceptBracket) {
                  accept('[');
              }
  
              boolean predicateFlag = false;
              int lparanCount = 0;
  
              if (ch == '?') {
                  next();
                  accept('(');
                  lparanCount++;
                  while (ch == '(') {
                      next();
                      lparanCount++;
                  }
                  predicateFlag = true;
              }
  
              skipWhitespace();
  
              if (predicateFlag
                      || IOUtils.firstIdentifier(ch)
                      || Character.isJavaIdentifierStart(ch)
                      || ch == '\\'
                      || ch == '@') {
                  boolean self = false;
                  if (ch == '@') {
                      next();
                      accept('.');
                      self = true;
                  }
                  String propertyName = readName();
  
                  skipWhitespace();
  
                  if (predicateFlag && ch == ')') {
                      next();
  
                      Filter filter = new NotNullSegement(propertyName, false);
                      while (ch == ' ') {
                          next();
                      }
  
                      if (ch == '&' || ch == '|') {
                          filter = filterRest(filter);
                      }
  
                      if (acceptBracket) {
                          accept(']');
                      }
                      return filter;
                  }
  
                  if (acceptBracket && ch == ']') {
                      if (isEOF()) {
                          if (propertyName.equals("last")) {
                              return new MultiIndexSegment(new int[]{-1});
                          }
                      }
  
                      next();
                      Filter filter = new NotNullSegement(propertyName, false);
                      while (ch == ' ') {
                          next();
                      }
  
                      if (ch == '&' || ch == '|') {
                          filter = filterRest(filter);
                      }
  
                      accept(')');
                      if (predicateFlag) {
                          accept(')');
                      }
  
                      if (acceptBracket) {
                          accept(']');
                      }
                      return filter;
                  }
  
                  boolean function = false;
                  skipWhitespace();
                  if (ch == '(') {
                      next();
                      accept(')');
                      skipWhitespace();
                      function = true;
                  }
  
                  Operator op = readOp();
  
                  skipWhitespace();
  
                  if (op == Operator.BETWEEN || op == Operator.NOT_BETWEEN) {
                      final boolean not = (op == Operator.NOT_BETWEEN);
  
                      Object startValue = readValue();
  
                      String name = readName();
  
                      if (!"and".equalsIgnoreCase(name)) {
                          throw new JSONPathException(path);
                      }
  
                      Object endValue = readValue();
  
                      if (startValue == null || endValue == null) {
                          throw new JSONPathException(path);
                      }
  
                      if (isInt(startValue.getClass()) && isInt(endValue.getClass())) {
                          Filter filter = new IntBetweenSegement(propertyName
                                  , function
                                  , TypeUtils.longExtractValue((Number) startValue)
                                  , TypeUtils.longExtractValue((Number) endValue)
                                  , not);
                          return filter;
                      }
  
                      throw new JSONPathException(path);
                  }
  
                  if (op == Operator.IN || op == Operator.NOT_IN) {
                      final boolean not = (op == Operator.NOT_IN);
                      accept('(');
  
                      List<Object> valueList = new JSONArray();
                      {
                          Object value = readValue();
                          valueList.add(value);
  
                          for (;;) {
                              skipWhitespace();
                              if (ch != ',') {
                                  break;
                              }
                              next();
  
                              value = readValue();
                              valueList.add(value);
                          }
                      }
  
                      boolean isInt = true;
                      boolean isIntObj = true;
                      boolean isString = true;
                      for (Object item : valueList) {
                          if (item == null) {
                              if (isInt) {
                                  isInt = false;
                              }
                              continue;
                          }
  
                          Class<?> clazz = item.getClass();
                          if (isInt && !(clazz == Byte.class || clazz == Short.class || clazz == Integer.class
                                         || clazz == Long.class)) {
                              isInt = false;
                              isIntObj = false;
                          }
  
                          if (isString && clazz != String.class) {
                              isString = false;
                          }
                      }
  
                      if (valueList.size() == 1 && valueList.get(0) == null) {
                          Filter filter;
                          if (not) {
                              filter = new NotNullSegement(propertyName, function);
                          } else {
                              filter = new NullSegement(propertyName, function);
                          }
  
                          while (ch == ' ') {
                              next();
                          }
  
                          if (ch == '&' || ch == '|') {
                              filter = filterRest(filter);
                          }
  
                          accept(')');
                          if (predicateFlag) {
                              accept(')');
                          }
  
                          if (acceptBracket) {
                              accept(']');
                          }
  
                          return filter;
                      }
  
                      if (isInt) {
                          if (valueList.size() == 1) {
                              long value = TypeUtils.longExtractValue((Number) valueList.get(0));
                              Operator intOp = not ? Operator.NE : Operator.EQ;
                              Filter filter = new IntOpSegement(propertyName, function, value, intOp);
                              while (ch == ' ') {
                                  next();
                              }
  
                              if (ch == '&' || ch == '|') {
                                  filter = filterRest(filter);
                              }
  
                              accept(')');
                              if (predicateFlag) {
                                  accept(')');
                              }
  
                              if (acceptBracket) {
                                  accept(']');
                              }
  
                              return filter;
                          }
  
                          long[] values = new long[valueList.size()];
                          for (int i = 0; i < values.length; ++i) {
                              values[i] = TypeUtils.longExtractValue((Number) valueList.get(i));
                          }
  
                          Filter filter = new IntInSegement(propertyName, function, values, not);
  
                          while (ch == ' ') {
                              next();
                          }
  
                          if (ch == '&' || ch == '|') {
                              filter = filterRest(filter);
                          }
  
                          accept(')');
                          if (predicateFlag) {
                              accept(')');
                          }
  
                          if (acceptBracket) {
                              accept(']');
                          }
  
                          return filter;
                      }
  
                      if (isString) {
                          if (valueList.size() == 1) {
                              String value = (String) valueList.get(0);
  
                              Operator intOp = not ? Operator.NE : Operator.EQ;
                              Filter filter = new StringOpSegement(propertyName, function, value, intOp);
  
                              while (ch == ' ') {
                                  next();
                              }
  
                              if (ch == '&' || ch == '|') {
                                  filter = filterRest(filter);
                              }
  
                              accept(')');
                              if (predicateFlag) {
                                  accept(')');
                              }
  
                              if (acceptBracket) {
                                  accept(']');
                              }
  
                              return filter;
                          }
  
                          String[] values = new String[valueList.size()];
                          valueList.toArray(values);
  
                          Filter filter = new StringInSegement(propertyName, function, values, not);
  
                          while (ch == ' ') {
                              next();
                          }
  
                          if (ch == '&' || ch == '|') {
                              filter = filterRest(filter);
                          }
  
                          accept(')');
                          if (predicateFlag) {
                              accept(')');
                          }
  
                          if (acceptBracket) {
                              accept(']');
                          }
  
                          return filter;
                      }
  
                      if (isIntObj) {
                          Long[] values = new Long[valueList.size()];
                          for (int i = 0; i < values.length; ++i) {
                              Number item = (Number) valueList.get(i);
                              if (item != null) {
                                  values[i] = TypeUtils.longExtractValue(item);
                              }
                          }
  
                          Filter filter = new IntObjInSegement(propertyName, function, values, not);
  
                          while (ch == ' ') {
                              next();
                          }
  
                          if (ch == '&' || ch == '|') {
                              filter = filterRest(filter);
                          }
  
                          accept(')');
                          if (predicateFlag) {
                              accept(')');
                          }
  
                          if (acceptBracket) {
                              accept(']');
                          }
  
                          return filter;
                      }
  
                      throw new UnsupportedOperationException();
                  }
  
                  if (ch == '\'' || ch == '"') {
                      String strValue = readString();
  
                      Filter filter = null;
                      if (op == Operator.RLIKE) {
                          filter = new RlikeSegement(propertyName, function, strValue, false);
                      } else if (op == Operator.NOT_RLIKE) {
                          filter = new RlikeSegement(propertyName, function, strValue, true);
                      } else  if (op == Operator.LIKE || op == Operator.NOT_LIKE) {
                          while (strValue.indexOf("%%") != -1) {
                              strValue = strValue.replaceAll("%%", "%");
                          }
  
                          final boolean not = (op == Operator.NOT_LIKE);
  
                          int p0 = strValue.indexOf('%');
                          if (p0 == -1) {
                              if (op == Operator.LIKE) {
                                  op = Operator.EQ;
                              } else {
                                  op = Operator.NE;
                              }
                              filter = new StringOpSegement(propertyName, function, strValue, op);
                          } else {
                              String[] items = strValue.split("%");
  
                              String startsWithValue = null;
                              String endsWithValue = null;
                              String[] containsValues = null;
                              if (p0 == 0) {
                                  if (strValue.charAt(strValue.length() - 1) == '%') {
                                      containsValues = new String[items.length - 1];
                                      System.arraycopy(items, 1, containsValues, 0, containsValues.length);
                                  } else {
                                      endsWithValue = items[items.length - 1];
                                      if (items.length > 2) {
                                          containsValues = new String[items.length - 2];
                                          System.arraycopy(items, 1, containsValues, 0, containsValues.length);
                                      }
                                  }
                              } else if (strValue.charAt(strValue.length() - 1) == '%') {
                                  if (items.length == 1) {
                                      startsWithValue = items[0];
                                  } else {
                                      containsValues = items;
                                  }
                              } else {
                                  if (items.length == 1) {
                                      startsWithValue = items[0];
                                  } else if (items.length == 2) {
                                      startsWithValue = items[0];
                                      endsWithValue = items[1];
                                  } else {
                                      startsWithValue = items[0];
                                      endsWithValue = items[items.length - 1];
                                      containsValues = new String[items.length - 2];
                                      System.arraycopy(items, 1, containsValues, 0, containsValues.length);
                                  }
                              }
  
                              filter = new MatchSegement(propertyName, function, startsWithValue, endsWithValue,
                                      containsValues, not);
                          }
                      } else {
                          filter = new StringOpSegement(propertyName, function, strValue, op);
                      }
  
                      while (ch == ' ') {
                          next();
                      }
  
                      if (ch == '&' || ch == '|') {
                          filter = filterRest(filter);
                      }
  
                      if (predicateFlag) {
                          accept(')');
                      }
                      
                      if (acceptBracket) {
                          accept(']');
                      }
  
                      return filter;
                  }
  
                  if (isDigitFirst(ch)) {
                      long value = readLongValue();
                      double doubleValue = 0D;
                      if (ch == '.') {
                          doubleValue = readDoubleValue(value);
                          
                      }
  
                      Filter filter;
  
                      if (doubleValue == 0) {
                          filter = new IntOpSegement(propertyName, function, value, op);
                      } else {
                          filter = new DoubleOpSegement(propertyName, function, doubleValue, op);
                      }
  
                      while (ch == ' ') {
                          next();
                      }
  
                      if (lparanCount > 1 && ch == ')') {
                          next();
                          lparanCount--;
                      }
  
                      if (ch == '&' || ch == '|') {
                          filter = filterRest(filter);
                      }
  
                      if (predicateFlag) {
                          lparanCount--;
                          accept(')');
                      }
  
                      if (acceptBracket) {
                          accept(']');
                      }
  
                      return filter;
                  } else if (ch == '$') {
                      Segment segment = readSegement();
                      RefOpSegement filter = new RefOpSegement(propertyName, function, segment, op);
                      hasRefSegment = true;
                      while (ch == ' ') {
                          next();
                      }
  
                      if (predicateFlag) {
                          accept(')');
                      }
  
                      if (acceptBracket) {
                          accept(']');
                      }
  
                      return filter;
                  } else if (ch == '/') {
                      int flags = 0;
                      StringBuilder regBuf = new StringBuilder();
                      for (;;) {
                          next();
                          if (ch == '/') {
                              next();
                              if (ch == 'i') {
                                  next();
                                  flags |= Pattern.CASE_INSENSITIVE;
                              }
                              break;
                          }
  
                          if (ch == '\\') {
                              next();
                              regBuf.append(ch);
                          } else {
                              regBuf.append(ch);
                          }
                      }
  
                      Pattern pattern = Pattern.compile(regBuf.toString(), flags);
                      RegMatchSegement filter = new RegMatchSegement(propertyName, function, pattern, op);
  
                      if (predicateFlag) {
                          accept(')');
                      }
  
                      if (acceptBracket) {
                          accept(']');
                      }
  
                      return filter;
                  }
  
                  if (ch == 'n') {
                      String name = readName();
                      if ("null".equals(name)) {
                          Filter filter = null;
                          if (op == Operator.EQ) {
                              filter = new NullSegement(propertyName, function);
                          } else if (op == Operator.NE) {
                              filter = new NotNullSegement(propertyName, function);
                          }
  
                          if (filter != null) {
                              while (ch == ' ') {
                                  next();
                              }
  
                              if (ch == '&' || ch == '|') {
                                  filter = filterRest(filter);
                              }
                          }
  
                          if (predicateFlag) {
                              accept(')');
                          }
                          accept(']');
  
                          if (filter != null) {
                              return filter;
                          }
  
                          throw new UnsupportedOperationException();
                      }
                  } else if (ch == 't') {
                      String name = readName();
                      
                      if ("true".equals(name)) {
                          Filter filter = null;
  
                          if (op == Operator.EQ) {
                              filter = new ValueSegment(propertyName, function, Boolean.TRUE, true);
                          } else if (op == Operator.NE) {
                              filter = new ValueSegment(propertyName, function, Boolean.TRUE, false);
                          }
  
                          if (filter != null) {
                              while (ch == ' ') {
                                  next();
                              }
  
                              if (ch == '&' || ch == '|') {
                                  filter = filterRest(filter);
                              }
                          }
  
                          if (predicateFlag) {
                              accept(')');
                          }
                          accept(']');
  
                          if (filter != null) {
                              return filter;
                          }
  
                          throw new UnsupportedOperationException();
                      }
                  } else if (ch == 'f') {
                      String name = readName();
                      
                      if ("false".equals(name)) {
                          Filter filter = null;
  
                          if (op == Operator.EQ) {
                              filter = new ValueSegment(propertyName, function, Boolean.FALSE, true);
                          } else if (op == Operator.NE) {
                              filter = new ValueSegment(propertyName, function, Boolean.FALSE, false);
                          }
  
                          if (filter != null) {
                              while (ch == ' ') {
                                  next();
                              }
  
                              if (ch == '&' || ch == '|') {
                                  filter = filterRest(filter);
                              }
                          }
  
                          if (predicateFlag) {
                              accept(')');
                          }
                          accept(']');
  
                          if (filter != null) {
                              return filter;
                          }
  
                          throw new UnsupportedOperationException();
                      }
                  }
  
                  throw new UnsupportedOperationException();
                  // accept(')');
              }
  
              int start = pos - 1;
              char startCh = ch;
              while (ch != ']' && ch != '/' && !isEOF()) {
                  if (ch == '.' //
                          && (!predicateFlag) // 
                          && !predicateFlag
                          && startCh != '\''
                  ) {
                      break;
                  }
                  
                  if (ch == '\\') {
                      next();
                  }
                  next();
              }
              
              int end;
              if (acceptBracket) {
                  end = pos - 1;
              } else {
                  if (ch == '/' || ch == '.') {
                      end = pos - 1;
                  } else {
                      end = pos;
                  }
              }
              
              String text = path.substring(start, end);
  
              if (text.indexOf('\\') != 0) {
                  StringBuilder buf = new StringBuilder(text.length());
                  for (int i = 0; i < text.length(); ++i) {
                      char ch = text.charAt(i);
                      if (ch == '\\' && i < text.length() - 1) {
                          char c2 = text.charAt(i + 1);
                          if (c2 == '@' || ch == '\\' || ch == '\"') {
                              buf.append(c2);
                              i++;
                              continue;
                          }
                      }
  
                      buf.append(ch);
                  }
                  text = buf.toString();
              }
              
              if (text.indexOf("\\.") != -1) {
                  String propName;
                  if (startCh == '\'' && text.length() > 2 && text.charAt(text.length() - 1) == startCh) {
                      propName = text.substring(1, text.length() - 1);
                  } else {
                      propName = text.replaceAll("\\\\\\.", "\\.");
                      if (propName.indexOf("\\-") != -1) {
                          propName = propName.replaceAll("\\\\-", "-");
                      }
                  }
  
                  if (predicateFlag) {
                      accept(')');
                  }
  
                  return new PropertySegment(propName, false);
              }
  
              Segment segment = buildArraySegement(text);
  
              if (acceptBracket && !isEOF()) {
                  accept(']');
              }
  
              return segment;
          }
  
          Filter filterRest(Filter filter) {
              boolean and = ch == '&';
              if ((ch == '&' && getNextChar() == '&') || (ch == '|' && getNextChar() == '|')) {
                  next();
                  next();
  
                  boolean paren = false;
                  if (ch == '(') {
                      paren = true;
                      next();
                  }
  
                  while (ch == ' ') {
                      next();
                  }
  
                  Filter right = (Filter) parseArrayAccessFilter(false);
  
                  filter = new FilterGroup(filter, right, and);
  
                  if (paren && ch == ')') {
                      next();
                  }
              }
              return filter;
          }
  
          protected long readLongValue() {
              int beginIndex = pos - 1;
              if (ch == '+' || ch == '-') {
                  next();
              }
  
              while (ch >= '0' && ch <= '9') {
                  next();
              }
  
              int endIndex = pos - 1;
              String text = path.substring(beginIndex, endIndex);
              long value = Long.parseLong(text);
              return value;
          }
          
          protected double readDoubleValue(long longValue) {
              int beginIndex = pos - 1;
  
              next();
              while (ch >= '0' && ch <= '9') {
                  next();
              }
  
              int endIndex = pos - 1;
              String text = path.substring(beginIndex, endIndex);
              double value = Double.parseDouble(text);
              value += longValue;
              return value;
          }
  
          protected Object readValue() {
              skipWhitespace();
  
              if (isDigitFirst(ch)) {
                  return readLongValue();
              }
  
              if (ch == '"' || ch == '\'') {
                  return readString();
              }
  
              if (ch == 'n') {
                  String name = readName();
  
                  if ("null".equals(name)) {
                      return null;
                  } else {
                      throw new JSONPathException(path);
                  }
              }
  
              throw new UnsupportedOperationException();
          }
  
          static boolean isDigitFirst(char ch) {
              return ch == '-' || ch == '+' || (ch >= '0' && ch <= '9');
          }
  
          protected Operator readOp() {
              Operator op = null;
              if (ch == '=') {
                  next();
                  if (ch == '~') {
                      next();
                      op = Operator.REG_MATCH;
                  } else if (ch == '=') {
                      next();
                      op = Operator.EQ;
                  } else {
                      op = Operator.EQ;
                  }
              } else if (ch == '!') {
                  next();
                  accept('=');
                  op = Operator.NE;
              } else if (ch == '<') {
                  next();
                  if (ch == '=') {
                      next();
                      op = Operator.LE;
                  } else {
                      op = Operator.LT;
                  }
              } else if (ch == '>') {
                  next();
                  if (ch == '=') {
                      next();
                      op = Operator.GE;
                  } else {
                      op = Operator.GT;
                  }
              }
  
              if (op == null) {
                  String name = readName();
  
                  if ("not".equalsIgnoreCase(name)) {
                      skipWhitespace();
  
                      name = readName();
  
                      if ("like".equalsIgnoreCase(name)) {
                          op = Operator.NOT_LIKE;
                      } else if ("rlike".equalsIgnoreCase(name)) {
                          op = Operator.NOT_RLIKE;
                      } else if ("in".equalsIgnoreCase(name)) {
                          op = Operator.NOT_IN;
                      } else if ("between".equalsIgnoreCase(name)) {
                          op = Operator.NOT_BETWEEN;
                      } else {
                          throw new UnsupportedOperationException();
                      }
                  } else if ("nin".equalsIgnoreCase(name)) {
                      op = Operator.NOT_IN;
                  } else {
                      if ("like".equalsIgnoreCase(name)) {
                          op = Operator.LIKE;
                      } else if ("rlike".equalsIgnoreCase(name)) {
                          op = Operator.RLIKE;
                      } else if ("in".equalsIgnoreCase(name)) {
                          op = Operator.IN;
                      } else if ("between".equalsIgnoreCase(name)) {
                          op = Operator.BETWEEN;
                      } else {
                          throw new UnsupportedOperationException();
                      }
                  }
              }
              return op;
          }
  
          String readName() {
              skipWhitespace();
  
              if (ch != '\\' && !Character.isJavaIdentifierStart(ch)) {
                  throw new JSONPathException("illeal jsonpath syntax. " + path);
              }
  
              StringBuilder buf = new StringBuilder();
              while (!isEOF()) {
                  if (ch == '\\') {
                      next();
                      buf.append(ch);
                      if (isEOF()) {
                          return buf.toString();
                      }
                      next();
                      continue;
                  }
  
                  boolean identifierFlag = Character.isJavaIdentifierPart(ch);
                  if (!identifierFlag) {
                      break;
                  }
                  buf.append(ch);
                  next();
              }
  
              if (isEOF() && Character.isJavaIdentifierPart(ch)) {
                  buf.append(ch);
              }
  
              return buf.toString();
          }
  
          String readString() {
              char quoate = ch;
              next();
  
              int beginIndex = pos - 1;
              while (ch != quoate && !isEOF()) {
                  next();
              }
  
              String strValue = path.substring(beginIndex, isEOF() ? pos : pos - 1);
  
              accept(quoate);
  
              return strValue;
          }
  
          void accept(char expect) {
              if (ch == ' ') {
                  next();
              }
  
              if (ch != expect) {
                  throw new JSONPathException("expect '" + expect + ", but '" + ch + "'");
              }
  
              if (!isEOF()) {
                  next();
              }
          }
  
          public Segment[] explain() {
              if (path == null || path.length() == 0) {
                  throw new IllegalArgumentException();
              }
  
              Segment[] segments = new Segment[8];
  
              for (;;) {
                  Segment segment = readSegement();
                  if (segment == null) {
                      break;
                  }
  
                  if (segment instanceof PropertySegment) {
                      PropertySegment propertySegment = (PropertySegment) segment;
                      if ((!propertySegment.deep) && propertySegment.propertyName.equals("*")) {
                          continue;
                      }
                  }
  
                  if (level == segments.length) {
                      Segment[] t = new Segment[level * 3 / 2];
                      System.arraycopy(segments, 0, t, 0, level);
                      segments = t;
                  }
                  segments[level++] = segment;
              }
  
              if (level == segments.length) {
                  return segments;
              }
  
              Segment[] result = new Segment[level];
              System.arraycopy(segments, 0, result, 0, level);
              return result;
          }
  
          Segment buildArraySegement(String indexText) {
              final int indexTextLen = indexText.length();
              final char firstChar = indexText.charAt(0);
              final char lastChar = indexText.charAt(indexTextLen - 1);
  
              int commaIndex = indexText.indexOf(',');
  
              if (indexText.length() > 2 && firstChar == '\'' && lastChar == '\'') {
  
                  String propertyName = indexText.substring(1, indexTextLen - 1);
  
                  if (commaIndex == -1 || !strArrayPatternx.matcher(indexText).find()) {
                      return new PropertySegment(propertyName, false);
                  }
  
                  String[] propertyNames = propertyName.split(strArrayRegex);
                  return new MultiPropertySegment(propertyNames);
              }
  
              int colonIndex = indexText.indexOf(':');
  
              if (commaIndex == -1 && colonIndex == -1) {
                  if (TypeUtils.isNumber(indexText)) {
                      try {
                          int index = Integer.parseInt(indexText);
                          return new ArrayAccessSegment(index);
                      }catch (NumberFormatException ex){
                          return new PropertySegment(indexText, false); // fix ISSUE-1208
                      }
                  } else {
                      if (indexText.charAt(0) == '"' && indexText.charAt(indexText.length() - 1) == '"') {
                          indexText = indexText.substring(1, indexText.length() - 1);
                      }
                      return new PropertySegment(indexText, false);
                  }
              }
  
              if (commaIndex != -1) {
                  String[] indexesText = indexText.split(",");
                  int[] indexes = new int[indexesText.length];
                  for (int i = 0; i < indexesText.length; ++i) {
                      indexes[i] = Integer.parseInt(indexesText[i]);
                  }
                  return new MultiIndexSegment(indexes);
              }
  
              if (colonIndex != -1) {
                  String[] indexesText = indexText.split(":");
                  int[] indexes = new int[indexesText.length];
                  for (int i = 0; i < indexesText.length; ++i) {
                      String str = indexesText[i];
                      if (str.length() == 0) {
                          if (i == 0) {
                              indexes[i] = 0;
                          } else {
                              throw new UnsupportedOperationException();
                          }
                      } else {
                          indexes[i] = Integer.parseInt(str);
                      }
                  }
  
                  int start = indexes[0];
                  int end;
                  if (indexes.length > 1) {
                      end = indexes[1];
                  } else {
                      end = -1;
                  }
                  int step;
                  if (indexes.length == 3) {
                      step = indexes[2];
                  } else {
                      step = 1;
                  }
  
                  if (end >= 0 && end < start) {
                      throw new UnsupportedOperationException("end must greater than or equals start. start " + start
                                                              + ",  end " + end);
                  }
  
                  if (step <= 0) {
                      throw new UnsupportedOperationException("step must greater than zero : " + step);
                  }
                  return new RangeSegment(start, end, step);
              }
  
              throw new UnsupportedOperationException();
          }
      }
  
      interface Segment {
  
          Object eval(JSONPath path, Object rootObject, Object currentObject);
          void extract(JSONPath path, DefaultJSONParser parser, Context context);
      }
  
  
      static class SizeSegment implements Segment {
          public final static SizeSegment instance = new SizeSegment();
          public Integer eval(JSONPath path, Object rootObject, Object currentObject) {
              return path.evalSize(currentObject);
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              Object object = parser.parse();
              context.object = path.evalSize(object);
          }
      }
  
      static class TypeSegment implements Segment {
          public final static TypeSegment instance = new TypeSegment();
  
          public String eval(JSONPath path, Object rootObject, Object currentObject) {
              if (currentObject == null) {
                  return "null";
              }
  
              if (currentObject instanceof Collection) {
                  return "array";
              }
  
              if (currentObject instanceof Number) {
                  return "number";
              }
  
              if (currentObject instanceof Boolean) {
                  return "boolean";
              }
  
              if (currentObject instanceof String
                      || currentObject instanceof UUID
                      || currentObject instanceof Enum) {
                  return "string";
              }
  
              return "object";
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static class FloorSegment implements Segment {
          public final static FloorSegment instance = new FloorSegment();
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              if (currentObject instanceof JSONArray) {
                  JSONArray array = ((JSONArray) ((JSONArray) currentObject).clone());
                  for (int i = 0; i < array.size(); i++) {
                      Object item = array.get(i);
                      Object newItem = floor(item);
                      if (newItem != item) {
                          array.set(i , newItem);
                      }
                  }
                  return array;
              }
  
              return floor(currentObject);
          }
  
          private static Object floor(Object item) {
              if (item == null) {
                  return null;
              }
  
              if (item instanceof Float) {
                  return Math.floor((Float) item);
              }
  
              if (item instanceof Double) {
                  return Math.floor((Double) item);
              }
  
              if (item instanceof BigDecimal) {
                  BigDecimal decimal = (BigDecimal) item;
                  return decimal.setScale(0, RoundingMode.FLOOR);
              }
  
              if (item instanceof Byte
                      || item instanceof Short
                      || item instanceof Integer
                      || item instanceof Long
                      || item instanceof BigInteger) {
                  return item;
              }
  
              throw new UnsupportedOperationException();
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static class MaxSegment implements Segment {
  
          public final static MaxSegment instance = new MaxSegment();
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              Object max = null;
              if (currentObject instanceof Collection) {
                  Iterator iterator = ((Collection) currentObject).iterator();
                  while (iterator.hasNext()) {
                      Object next = iterator.next();
                      if (next == null) {
                          continue;
                      }
  
                      if (max == null) {
                          max = next;
                      } else if (compare(max, next) < 0) {
                          max = next;
                      }
                  }
              } else {
                  throw new UnsupportedOperationException();
              }
  
              return max;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static class MinSegment implements Segment {
          public final static MinSegment instance = new MinSegment();
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              Object min = null;
              if (currentObject instanceof Collection) {
                  Iterator iterator = ((Collection) currentObject).iterator();
                  while (iterator.hasNext()) {
                      Object next = iterator.next();
                      if (next == null) {
                          continue;
                      }
  
                      if (min == null) {
                          min = next;
                      } else if (compare(min, next) > 0) {
                          min = next;
                      }
                  }
              } else {
                  throw new UnsupportedOperationException();
              }
  
              return min;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static int compare(Object a, Object b) {
          if (a.getClass() == b.getClass()) {
              return ((Comparable) a).compareTo(b);
          }
  
          Class typeA = a.getClass();
          Class typeB = b.getClass();
  
          if (typeA == BigDecimal.class) {
              if (typeB == Integer.class) {
                  b = new BigDecimal((Integer) b);
              } else if (typeB == Long.class) {
                  b = new BigDecimal((Long) b);
              } else if (typeB == Float.class) {
                  b = new BigDecimal((Float) b);
              } else if (typeB == Double.class) {
                  b = new BigDecimal((Double) b);
              }
          } else if (typeA == Long.class) {
              if (typeB == Integer.class) {
                  b = new Long((Integer) b);
              } else if (typeB == BigDecimal.class) {
                  a = new BigDecimal((Long) a);
              } else if (typeB == Float.class) {
                  a = new Float((Long) a);
              } else if (typeB == Double.class) {
                  a = new Double((Long) a);
              }
          } else if (typeA == Integer.class) {
              if (typeB == Long.class) {
                  a = new Long((Integer) a);
              } else if (typeB == BigDecimal.class) {
                  a = new BigDecimal((Integer) a);
              } else if (typeB == Float.class) {
                  a = new Float((Integer) a);
              } else if (typeB == Double.class) {
                  a = new Double((Integer) a);
              }
          } else if (typeA == Double.class) {
              if (typeB == Integer.class) {
                  b = new Double((Integer) b);
              } else if (typeB == Long.class) {
                  b = new Double((Long) b);
              } else if (typeB == Float.class) {
                  b = new Double((Float) b);
              }
          } else if (typeA == Float.class) {
              if (typeB == Integer.class) {
                  b = new Float((Integer) b);
              } else if (typeB == Long.class) {
                  b = new Float((Long) b);
              } else if (typeB == Double.class) {
                  a = new Double((Float) a);
              }
          }
  
          return ((Comparable) a).compareTo(b);
      }
  
      static class KeySetSegment implements Segment {
  
          public final static KeySetSegment instance = new KeySetSegment();
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              return path.evalKeySet(currentObject);
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static class PropertySegment implements Segment {
  
          private final String  propertyName;
          private final long    propertyNameHash;
          private final boolean deep;
  
          public PropertySegment(String propertyName, boolean deep){
              this.propertyName = propertyName;
              this.propertyNameHash = TypeUtils.fnv1a_64(propertyName);
              this.deep = deep;
          }
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              if (deep) {
                  List<Object> results = new ArrayList<Object>();
                  path.deepScan(currentObject, propertyName, results);
                  return results;
              } else {
                  // return path.getPropertyValue(currentObject, propertyName, true);
                  return path.getPropertyValue(currentObject, propertyName, propertyNameHash);
              }
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              JSONLexerBase lexer = (JSONLexerBase) parser.lexer;
  
              if (deep && context.object == null) {
                  context.object = new JSONArray();
              }
  
              if (lexer.token() == JSONToken.LBRACKET) {
                  if ("*".equals(propertyName)) {
                      return;
                  }
  
                  lexer.nextToken();
                  JSONArray array;
  
                  if (deep) {
                      array =(JSONArray) context.object;
                  } else {
                      array = new JSONArray();
                  }
                  for (;;) {
                      switch (lexer.token()) {
                          case JSONToken.LBRACE: {
                              if (deep) {
                                  extract(path, parser, context);
                                  break;
                              }
                              int matchStat = lexer.seekObjectToField(propertyNameHash, deep);
                              if (matchStat == JSONLexer.VALUE) {
                                  Object value;
                                  switch (lexer.token()) {
                                      case JSONToken.LITERAL_INT:
                                          value = lexer.integerValue();
                                          lexer.nextToken();
                                          break;
                                      case JSONToken.LITERAL_STRING:
                                          value = lexer.stringVal();
                                          lexer.nextToken();
                                          break;
                                      default:
                                          value = parser.parse();
                                          break;
                                  }
  
                                  array.add(value);
                                  if (lexer.token() == JSONToken.RBRACE) {
                                      lexer.nextToken();
                                      continue;
                                  } else {
                                      lexer.skipObject(false);
                                  }
                              } else if (matchStat == JSONLexer.NOT_MATCH) {
                                  continue;
                              } else {
                                  if (deep) {
                                      throw new UnsupportedOperationException(lexer.info());
                                  } else {
                                      lexer.skipObject(false);
                                  }
                              }
                              break;
                          }
                          case JSONToken.LBRACKET:
                              if (deep) {
                                  extract(path, parser, context);
                              } else {
                                  lexer.skipObject(false);
                              }
                              break;
                          case JSONToken.LITERAL_STRING:
                          case JSONToken.LITERAL_INT:
                          case JSONToken.LITERAL_FLOAT:
                          case JSONToken.LITERAL_ISO8601_DATE:
                          case JSONToken.TRUE:
                          case JSONToken.FALSE:
                          case JSONToken.NULL:
                              lexer.nextToken();
                              break;
                          default:
                              break;
                      }
  
                      if (lexer.token() == JSONToken.RBRACKET) {
                          lexer.nextToken();
                          break;
                      } else if (lexer.token() == JSONToken.COMMA) {
                          lexer.nextToken();
                          continue;
                      } else {
                          throw new JSONException("illegal json : " + lexer.info());
                      }
                  }
  
                  if (!deep) {
                      if (array.size() > 0) {
                          context.object = array;
                      }
                  }
                  return;
              }
  
              if (!deep) {
                  int matchStat = lexer.seekObjectToField(propertyNameHash, deep);
                  if (matchStat == JSONLexer.VALUE) {
                      if (context.eval) {
                          Object value;
                          switch (lexer.token()) {
                              case JSONToken.LITERAL_INT:
                                  value = lexer.integerValue();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              case JSONToken.LITERAL_FLOAT:
                                  value = lexer.decimalValue();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              case JSONToken.LITERAL_STRING:
                                  value = lexer.stringVal();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              default:
                                  value = parser.parse();
                                  break;
                          }
  
                          if (context.eval) {
                              context.object = value;
                          }
                      }
                  }
                  return;
              }
  
              // deep
              for (;;) {
                  int matchStat = lexer.seekObjectToField(propertyNameHash, deep);
                  if (matchStat == JSONLexer.NOT_MATCH) {
                      break;
                  }
  
                  if (matchStat == JSONLexer.VALUE) {
                      if (context.eval) {
                          Object value;
                          switch (lexer.token()) {
                              case JSONToken.LITERAL_INT:
                                  value = lexer.integerValue();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              case JSONToken.LITERAL_FLOAT:
                                  value = lexer.decimalValue();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              case JSONToken.LITERAL_STRING:
                                  value = lexer.stringVal();
                                  lexer.nextToken(JSONToken.COMMA);
                                  break;
                              default:
                                  value = parser.parse();
                                  break;
                          }
  
                          if (context.eval) {
                              if (context.object instanceof List) {
                                  List list = (List) context.object;
                                  if (list.size() == 0 && value instanceof List) {
                                      context.object = value;
                                  } else {
                                      list.add(value);
                                  }
                              } else {
                                  context.object = value;
                              }
                          }
                      }
                  } else if (matchStat == JSONLexer.OBJECT || matchStat == JSONLexer.ARRAY) {
                      extract(path, parser, context);
                  }
              }
          }
  
          public void setValue(JSONPath path, Object parent, Object value) {
              if (deep) {
                  path.deepSet(parent, propertyName, propertyNameHash, value);
              } else {
                  path.setPropertyValue(parent, propertyName, propertyNameHash, value);
              }
          }
          
          public boolean remove(JSONPath path, Object parent) {
              return path.removePropertyValue(parent, propertyName, deep);
          }
      }
  
      static class MultiPropertySegment implements Segment {
  
          private final String[] propertyNames;
          private final long[]   propertyNamesHash;
  
          public MultiPropertySegment(String[] propertyNames){
              this.propertyNames = propertyNames;
              this.propertyNamesHash = new long[propertyNames.length];
              for (int i = 0; i < propertyNamesHash.length; i++) {
                  propertyNamesHash[i] = TypeUtils.fnv1a_64(propertyNames[i]);
              }
          }
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              List<Object> fieldValues = new ArrayList<Object>(propertyNames.length);
  
              for (int i = 0; i < propertyNames.length; i++) {
                  Object fieldValue = path.getPropertyValue(currentObject, propertyNames[i], propertyNamesHash[i]);
                  fieldValues.add(fieldValue);
              }
  
              return fieldValues;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              JSONLexerBase lexer = (JSONLexerBase) parser.lexer;
  
              JSONArray array;
              if (context.object == null) {
                  context.object = array = new JSONArray();
              } else {
                  array = (JSONArray) context.object;
              }
              for (int i = array.size(); i < propertyNamesHash.length; ++i) {
                  array.add(null);
              }
  
  //            if (lexer.token() == JSONToken.LBRACKET) {
  //                lexer.nextToken();
  //                JSONArray array;
  //
  //                array = new JSONArray();
  //                for (;;) {
  //                    if (lexer.token() == JSONToken.LBRACE) {
  //                        int index = lexer.seekObjectToField(propertyNamesHash);
  //                        int matchStat = lexer.matchStat;
  //                        if (matchStat == JSONLexer.VALUE) {
  //                            Object value;
  //                            switch (lexer.token()) {
  //                                case JSONToken.LITERAL_INT:
  //                                    value = lexer.integerValue();
  //                                    lexer.nextToken();
  //                                    break;
  //                                case JSONToken.LITERAL_STRING:
  //                                    value = lexer.stringVal();
  //                                    lexer.nextToken();
  //                                    break;
  //                                default:
  //                                    value = parser.parse();
  //                                    break;
  //                            }
  //
  //                            array.add(index, value);
  //                            if (lexer.token() == JSONToken.RBRACE) {
  //                                lexer.nextToken();
  //                                continue;
  //                            } else {
  //                                lexer.skipObject();
  //                            }
  //                        } else {
  //                            lexer.skipObject();
  //                        }
  //                    }
  //
  //                    if (lexer.token() == JSONToken.RBRACKET) {
  //                        break;
  //                    } else if (lexer.token() == JSONToken.COMMA) {
  //                        lexer.nextToken();
  //                        continue;
  //                    } else {
  //                        throw new JSONException("illegal json.");
  //                    }
  //                }
  //
  //                context.object = array;
  //                return;
  //            }
  
              for_:
              for (;;) {
                  int index = lexer.seekObjectToField(propertyNamesHash);
                  int matchStat = lexer.matchStat;
                  if (matchStat == JSONLexer.VALUE) {
                      Object value;
                      switch (lexer.token()) {
                          case JSONToken.LITERAL_INT:
                              value = lexer.integerValue();
                              lexer.nextToken(JSONToken.COMMA);
                              break;
                          case JSONToken.LITERAL_FLOAT:
                              value = lexer.decimalValue();
                              lexer.nextToken(JSONToken.COMMA);
                              break;
                          case JSONToken.LITERAL_STRING:
                              value = lexer.stringVal();
                              lexer.nextToken(JSONToken.COMMA);
                              break;
                          default:
                              value = parser.parse();
                              break;
                      }
  
                      array.set(index, value);
  
                      if (lexer.token() == JSONToken.COMMA) {
                          continue for_;
                      }
                  }
  
                  break;
              }
          }
      }
  
      static class WildCardSegment implements Segment {
          private boolean deep;
          private boolean objectOnly;
  
          private WildCardSegment(boolean deep, boolean objectOnly) {
              this.deep = deep;
              this.objectOnly = objectOnly;
          }
  
          public final static WildCardSegment instance = new WildCardSegment(false, false);
          public final static WildCardSegment instance_deep = new WildCardSegment(true, false);
          public final static WildCardSegment instance_deep_objectOnly = new WildCardSegment(true, true);
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              if (!deep) {
                  return path.getPropertyValues(currentObject);
              }
  
              List<Object> values = new ArrayList<Object>();
              path.deepGetPropertyValues(currentObject, values);
              return values;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              if (context.eval) {
                  Object object = parser.parse();
                  if (deep) {
                      List<Object> values = new ArrayList<Object>();
                      if (objectOnly) {
                          path.deepGetObjects(object, values);
                      } else {
                          path.deepGetPropertyValues(object, values);
                      }
                      context.object = values;
                      return;
                  }
  
                  if (object instanceof JSONObject) {
                      Collection<Object> values = ((JSONObject) object).values();
                      JSONArray array = new JSONArray(values.size());
                      array.addAll(values);
                      context.object = array;
                      return;
                  } else if (object instanceof JSONArray) {
                      context.object = object;
                      return;
                  }
              }
  
              throw new JSONException("TODO");
          }
      }
  
      static class ArrayAccessSegment implements Segment {
  
          private final int index;
  
          public ArrayAccessSegment(int index){
              this.index = index;
          }
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              return path.getArrayItem(currentObject, index);
          }
  
          public boolean setValue(JSONPath path, Object currentObject, Object value) {
              return path.setArrayItem(path, currentObject, index, value);
          }
          
          public boolean remove(JSONPath path, Object currentObject) {
              return path.removeArrayItem(path, currentObject, index);
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              JSONLexerBase lexer = (JSONLexerBase) parser.lexer;
              if (lexer.seekArrayToItem(index)
                      && context.eval)
              {
                  context.object = parser.parse();
              }
          }
      }
  
      static class MultiIndexSegment implements Segment {
  
          private final int[] indexes;
  
          public MultiIndexSegment(int[] indexes){
              this.indexes = indexes;
          }
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              List<Object> items = new JSONArray(indexes.length);
              for (int i = 0; i < indexes.length; ++i) {
                  Object item = path.getArrayItem(currentObject, indexes[i]);
                  items.add(item);
              }
              return items;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              if (context.eval) {
                  Object object = parser.parse();
                  if (object instanceof List) {
                      int[] indexes = new int[this.indexes.length];
                      System.arraycopy(this.indexes, 0, indexes, 0, indexes.length);
                      boolean noneNegative = indexes[0] >= 0;
  
                      List list = (List) object;
                      if (noneNegative) {
                          for (int i = list.size() - 1; i >= 0; i--) {
                              if (Arrays.binarySearch(indexes, i) < 0) {
                                  list.remove(i);
                              }
                          }
                          context.object = list;
                          return;
                      }
                  }
              }
              throw new UnsupportedOperationException();
          }
      }
  
      static class RangeSegment implements Segment {
          private final int start;
          private final int end;
          private final int step;
  
          public RangeSegment(int start, int end, int step){
              this.start = start;
              this.end = end;
              this.step = step;
          }
  
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              int size = SizeSegment.instance.eval(path, rootObject, currentObject);
              int start = this.start >= 0 ? this.start : this.start + size;
              int end = this.end >= 0 ? this.end : this.end + size;
  
              int array_size = (end - start) / step + 1;
              if (array_size == -1) {
                  return null;
              }
  
              List<Object> items = new ArrayList<Object>(array_size);
              for (int i = start; i <= end && i < size; i += step) {
                  Object item = path.getArrayItem(currentObject, i);
                  items.add(item);
              }
              return items;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              throw new UnsupportedOperationException();
          }
      }
  
      static class NotNullSegement extends PropertyFilter {
          public NotNullSegement(String propertyName, boolean function){
              super(propertyName, function);
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              return path.getPropertyValue(item, propertyName, propertyNameHash) != null;
          }
      }
  
      static class NullSegement extends PropertyFilter {
          public NullSegement(String propertyName, boolean function){
              super(propertyName, function);
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              return propertyValue == null;
          }
      }
      
      static class ValueSegment extends PropertyFilter {
          private final Object value;
          private boolean eq = true;
          
          public ValueSegment(String propertyName,boolean function, Object value, boolean eq){
              super(propertyName, function);
  
              if (value == null) {
                  throw new IllegalArgumentException("value is null");
              }
              this.value = value;
              this.eq = eq;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
              boolean result = value.equals(propertyValue);
              if (!eq) {
                  result = !result;
              }
              return result;
          }
      }
  
      static class IntInSegement extends PropertyFilter {
          private final long[]  values;
          private final boolean not;
  
          public IntInSegement(String propertyName, boolean function, long[] values, boolean not){
              super(propertyName, function);
              this.values = values;
              this.not = not;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              if (propertyValue instanceof Number) {
                  long longPropertyValue = TypeUtils.longExtractValue((Number) propertyValue);
                  for (long value : values) {
                      if (value == longPropertyValue) {
                          return !not;
                      }
                  }
              }
  
              return not;
          }
      }
  
      static class IntBetweenSegement extends PropertyFilter {
          private final long    startValue;
          private final long    endValue;
          private final boolean not;
  
          public IntBetweenSegement(String propertyName, boolean function, long startValue, long endValue, boolean not){
              super(propertyName, function);
              this.startValue = startValue;
              this.endValue = endValue;
              this.not = not;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              if (propertyValue instanceof Number) {
                  long longPropertyValue = TypeUtils.longExtractValue((Number) propertyValue);
                  if (longPropertyValue >= startValue && longPropertyValue <= endValue) {
                      return !not;
                  }
              }
  
              return not;
          }
      }
  
      static class IntObjInSegement extends PropertyFilter {
          private final Long[]  values;
          private final boolean not;
  
          public IntObjInSegement(String propertyName, boolean function, Long[] values, boolean not){
              super(propertyName, function);
              this.values = values;
              this.not = not;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  for (Long value : values) {
                      if (value == null) {
                          return !not;
                      }
                  }
  
                  return not;
              }
  
              if (propertyValue instanceof Number) {
                  long longPropertyValue = TypeUtils.longExtractValue((Number) propertyValue);
                  for (Long value : values) {
                      if (value == null) {
                          continue;
                      }
  
                      if (value.longValue() == longPropertyValue) {
                          return !not;
                      }
                  }
              }
  
              return not;
          }
      }
  
      static class StringInSegement extends PropertyFilter {
          private final String[] values;
          private final boolean  not;
  
          public StringInSegement(String propertyName, boolean function, String[] values, boolean not){
              super(propertyName, function);
              this.values = values;
              this.not = not;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              for (String value : values) {
                  if (value == propertyValue) {
                      return !not;
                  } else if (value != null && value.equals(propertyValue)) {
                      return !not;
                  }
              }
  
              return not;
          }
      }
  
      static class IntOpSegement extends PropertyFilter {
          private final long     value;
          private final Operator op;
  
          private BigDecimal     valueDecimal;
          private Float          valueFloat;
          private Double         valueDouble;
  
          public IntOpSegement(String propertyName, boolean function, long value, Operator op){
              super(propertyName, function);
              this.value = value;
              this.op = op;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              if (!(propertyValue instanceof Number)) {
                  return false;
              }
  
              if (propertyValue instanceof BigDecimal) {
                  if (valueDecimal == null) {
                      valueDecimal = BigDecimal.valueOf(value);
                  }
  
                  int result = valueDecimal.compareTo((BigDecimal) propertyValue);
                  switch (op) {
                      case EQ:
                          return result == 0;
                      case NE:
                          return result != 0;
                      case GE:
                          return 0 >= result;
                      case GT:
                          return 0 > result;
                      case LE:
                          return 0 <= result;
                      case LT:
                          return 0 < result;
                  }
  
                  return false;
              }
  
              if (propertyValue instanceof Float) {
                  if (valueFloat == null) {
                      valueFloat = Float.valueOf(value);
                  }
  
                  int result = valueFloat.compareTo((Float) propertyValue);
                  switch (op) {
                      case EQ:
                          return result == 0;
                      case NE:
                          return result != 0;
                      case GE:
                          return 0 >= result;
                      case GT:
                          return 0 > result;
                      case LE:
                          return 0 <= result;
                      case LT:
                          return 0 < result;
                  }
  
                  return false;
              }
  
              if (propertyValue instanceof Double) {
                  if (valueDouble == null) {
                      valueDouble = Double.valueOf(value);
                  }
  
                  int result = valueDouble.compareTo((Double) propertyValue);
                  switch (op) {
                      case EQ:
                          return result == 0;
                      case NE:
                          return result != 0;
                      case GE:
                          return 0 >= result;
                      case GT:
                          return 0 > result;
                      case LE:
                          return 0 <= result;
                      case LT:
                          return 0 < result;
                  }
  
                  return false;
              }
  
              long longValue = TypeUtils.longExtractValue((Number) propertyValue);
  
              switch (op) {
                  case EQ:
                      return longValue == value;
                  case NE:
                      return longValue != value;
                  case GE:
                      return longValue >= value;
                  case GT:
                      return longValue > value;
                  case LE:
                      return longValue <= value;
                  case LT:
                      return longValue < value;
              }
  
              return false;
          }
      }
  
      static abstract class PropertyFilter implements Filter {
          static long TYPE = TypeUtils.fnv1a_64("type");
  
          protected final String  propertyName;
          protected final long    propertyNameHash;
          protected final boolean function;
          protected Segment functionExpr;
  
          protected PropertyFilter(String propertyName, boolean function) {
              this.propertyName = propertyName;
              this.propertyNameHash = TypeUtils.fnv1a_64(propertyName);
              this.function = function;
  
              if (function) {
                  if (propertyNameHash == TYPE) {
                      functionExpr = TypeSegment.instance;
                  } else if (propertyNameHash == SIZE) {
                      functionExpr = SizeSegment.instance;
                  } else {
                      throw new JSONPathException("unsupported funciton : " + propertyName);
                  }
              }
          }
  
          protected Object get(JSONPath path, Object rootObject, Object currentObject) {
              if (functionExpr != null) {
                  return functionExpr.eval(path, rootObject, currentObject);
              }
              return path.getPropertyValue(currentObject, propertyName, propertyNameHash);
          }
      }
      
      static class DoubleOpSegement extends PropertyFilter {
          private final double   value;
          private final Operator op;
  
          public DoubleOpSegement(String propertyName, boolean function, double value, Operator op){
              super(propertyName, function);
              this.value = value;
              this.op = op;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              if (!(propertyValue instanceof Number)) {
                  return false;
              }
  
              double doubleValue = ((Number) propertyValue).doubleValue();
  
              switch (op) {
                  case EQ:
                      return doubleValue == value;
                  case NE:
                      return doubleValue != value;
                  case GE:
                      return doubleValue >= value;
                  case GT:
                      return doubleValue > value;
                  case LE:
                      return doubleValue <= value;
                  case LT:
                      return doubleValue < value;
              }
  
              return false;
          }
      }
  
      static class RefOpSegement extends PropertyFilter {
          private final Segment  refSgement;
          private final Operator  op;
  
          public RefOpSegement(String propertyName, boolean function, Segment refSgement, Operator op){
              super(propertyName, function);
              this.refSgement = refSgement;
              this.op = op;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              if (!(propertyValue instanceof Number)) {
                  return false;
              }
  
              Object refValue = refSgement.eval(path, rootObject, rootObject);
  
              if (refValue instanceof Integer || refValue instanceof Long || refValue instanceof Short || refValue instanceof Byte) {
                  long value = TypeUtils.longExtractValue((Number) refValue);
  
                  if (propertyValue instanceof Integer || propertyValue instanceof Long || propertyValue instanceof Short || propertyValue instanceof Byte) {
                      long longValue = TypeUtils.longExtractValue((Number) propertyValue);
  
                      switch (op) {
                          case EQ:
                              return longValue == value;
                          case NE:
                              return longValue != value;
                          case GE:
                              return longValue >= value;
                          case GT:
                              return longValue > value;
                          case LE:
                              return longValue <= value;
                          case LT:
                              return longValue < value;
                      }
                  } else if (propertyValue instanceof BigDecimal) {
                      BigDecimal valueDecimal = BigDecimal.valueOf(value);
  
                      int result = valueDecimal.compareTo((BigDecimal) propertyValue);
                      switch (op) {
                          case EQ:
                              return result == 0;
                          case NE:
                              return result != 0;
                          case GE:
                              return 0 >= result;
                          case GT:
                              return 0 > result;
                          case LE:
                              return 0 <= result;
                          case LT:
                              return 0 < result;
                      }
  
                      return false;
                  }
              }
  
              throw new UnsupportedOperationException();
          }
      }
  
      static class MatchSegement extends PropertyFilter {
          private final String   startsWithValue;
          private final String   endsWithValue;
          private final String[] containsValues;
          private final int      minLength;
          private final boolean  not;
  
          public MatchSegement(
                  String propertyName,
                  boolean function,
                  String startsWithValue,
                  String endsWithValue,
                  String[] containsValues,
                  boolean not)
          {
              super(propertyName, function);
              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;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              final String strPropertyValue = propertyValue.toString();
  
              if (strPropertyValue.length() < minLength) {
                  return not;
              }
  
              int start = 0;
              if (startsWithValue != null) {
                  if (!strPropertyValue.startsWith(startsWithValue)) {
                      return not;
                  }
                  start += startsWithValue.length();
              }
  
              if (containsValues != null) {
                  for (String containsValue : containsValues) {
                      int index = strPropertyValue.indexOf(containsValue, start);
                      if (index == -1) {
                          return not;
                      }
                      start = index + containsValue.length();
                  }
              }
  
              if (endsWithValue != null) {
                  if (!strPropertyValue.endsWith(endsWithValue)) {
                      return not;
                  }
              }
  
              return !not;
          }
      }
  
      static class RlikeSegement extends PropertyFilter {
          private final Pattern pattern;
          private final boolean not;
  
          public RlikeSegement(String propertyName, boolean function, String pattern, boolean not){
              super(propertyName, function);
              this.pattern = Pattern.compile(pattern);
              this.not = not;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (propertyValue == null) {
                  return false;
              }
  
              String strPropertyValue = propertyValue.toString();
              Matcher m = pattern.matcher(strPropertyValue);
              boolean match = m.matches();
  
              if (not) {
                  match = !match;
              }
  
              return match;
          }
      }
  
      static class StringOpSegement extends PropertyFilter {
          private final String   value;
          private final Operator op;
  
          public StringOpSegement(String propertyName, boolean function, String value, Operator op){
              super(propertyName, function);
              this.value = value;
              this.op = op;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
  
              if (op == Operator.EQ) {
                  return value.equals(propertyValue);
              } else if (op == Operator.NE) {
                  return !value.equals(propertyValue);
              }
  
              if (propertyValue == null) {
                  return false;
              }
  
              int compareResult = value.compareTo(propertyValue.toString());
              if (op == Operator.GE) {
                  return compareResult <= 0;
              } else if (op == Operator.GT) {
                  return compareResult < 0;
              } else if (op == Operator.LE) {
                  return compareResult >= 0;
              } else if (op == Operator.LT) {
                  return compareResult > 0;
              }
  
              return false;
          }
      }
  
      static class RegMatchSegement extends PropertyFilter {
          private final Pattern  pattern;
          private final Operator op;
  
          public RegMatchSegement(String propertyName, boolean function, Pattern pattern, Operator op){
              super(propertyName, function);
              this.pattern = pattern;
              this.op = op;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              Object propertyValue = get(path, rootObject, item);
              if (propertyValue == null) {
                  return false;
              }
  
              String str = propertyValue.toString();
  
              Matcher m = pattern.matcher(str);
              return m.matches();
          }
      }
  
      enum Operator {
                     EQ, NE, GT, GE, LT, LE, LIKE, NOT_LIKE, RLIKE, NOT_RLIKE, IN, NOT_IN, BETWEEN, NOT_BETWEEN, And, Or, REG_MATCH
      }
  
      static public class FilterSegment implements Segment {
  
          private final Filter filter;
  
          public FilterSegment(Filter filter){
              super();
              this.filter = filter;
          }
  
          @SuppressWarnings("rawtypes")
          public Object eval(JSONPath path, Object rootObject, Object currentObject) {
              if (currentObject == null) {
                  return null;
              }
  
              List<Object> items = new JSONArray();
  
              if (currentObject instanceof Iterable) {
                  Iterator it = ((Iterable) currentObject).iterator();
                  while (it.hasNext()) {
                      Object item = it.next();
  
                      if (filter.apply(path, rootObject, currentObject, item)) {
                          items.add(item);
                      }
                  }
  
                  return items;
              }
  
              if (filter.apply(path, rootObject, currentObject, currentObject)) {
                  return currentObject;
              }
  
              return null;
          }
  
          public void extract(JSONPath path, DefaultJSONParser parser, Context context) {
              Object object = parser.parse();
              context.object = eval(path, object, object);
          }
  
          public boolean remove(JSONPath path, Object rootObject, Object currentObject) {
              if (currentObject == null) {
                  return false;
              }
  
              if (currentObject instanceof Iterable) {
                  Iterator it = ((Iterable) currentObject).iterator();
                  while (it.hasNext()) {
                      Object item = it.next();
  
                      if (filter.apply(path, rootObject, currentObject, item)) {
                          it.remove();
                      }
                  }
  
                  return true;
              }
  
              return false;
          }
      }
  
      interface Filter {
          boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item);
      }
  
      static class FilterGroup implements Filter {
          private boolean and;
          private List<Filter> fitlers;
  
          public FilterGroup(Filter left, Filter right, boolean and) {
              fitlers = new ArrayList<Filter>(2);
              fitlers.add(left);
              fitlers.add(right);
              this.and = and;
          }
  
          public boolean apply(JSONPath path, Object rootObject, Object currentObject, Object item) {
              if (and) {
                  for (Filter fitler : this.fitlers) {
                      if (!fitler.apply(path, rootObject, currentObject, item)) {
                          return false;
                      }
                  }
                  return true;
              } else {
                  for (Filter fitler : this.fitlers) {
                      if (fitler.apply(path, rootObject, currentObject, item)) {
                          return true;
                      }
                  }
                  return false;
              }
          }
      }
  
      @SuppressWarnings("rawtypes")
      protected Object getArrayItem(final Object currentObject, int index) {
          if (currentObject == null) {
              return null;
          }
  
          if (currentObject instanceof List) {
              List list = (List) currentObject;
  
              if (index >= 0) {
                  if (index < list.size()) {
                      return list.get(index);
                  }
                  return null;
              } else {
                  if (Math.abs(index) <= list.size()) {
                      return list.get(list.size() + index);
                  }
                  return null;
              }
          }
  
          if (currentObject.getClass().isArray()) {
              int arrayLenth = Array.getLength(currentObject);
  
              if (index >= 0) {
                  if (index < arrayLenth) {
                      return Array.get(currentObject, index);
                  }
                  return null;
              } else {
                  if (Math.abs(index) <= arrayLenth) {
                      return Array.get(currentObject, arrayLenth + index);
                  }
                  return null;
              }
          }
  
          if (currentObject instanceof Map) {
              Map map = (Map) currentObject;
              Object value = map.get(index);
              if (value == null) {
                  value = map.get(Integer.toString(index));
              }
              return value;
          }
  
          if (currentObject instanceof Collection) {
              Collection collection = (Collection) currentObject;
              int i = 0;
              for (Object item : collection) {
                  if (i == index) {
                      return item;
                  }
                  i++;
              }
              return null;
          }
  
          if (index == 0) {
              return currentObject;
          }
  
          throw new UnsupportedOperationException();
      }
  
      @SuppressWarnings({ "unchecked", "rawtypes" })
      public boolean setArrayItem(JSONPath path, Object currentObject, int index, Object value) {
          if (currentObject instanceof List) {
              List list = (List) currentObject;
              if (index >= 0) {
                  list.set(index, value);
              } else {
                  list.set(list.size() + index, value);
              }
              return true;
          }
  
          Class<?> clazz = currentObject.getClass();
          if (clazz.isArray()) {
              int arrayLenth = Array.getLength(currentObject);
  
              if (index >= 0) {
                  if (index < arrayLenth) {
                      Array.set(currentObject, index, value);
                  }
              } else {
                  if (Math.abs(index) <= arrayLenth) {
                      Array.set(currentObject, arrayLenth + index, value);
                  }
              }
  
              return true;
          }
  
          throw new JSONPathException("unsupported set operation." + clazz);
      }
  
      @SuppressWarnings("rawtypes")
      public boolean removeArrayItem(JSONPath path, Object currentObject, int index) {
          if (currentObject instanceof List) {
              List list = (List) currentObject;
              if (index >= 0) {
                  if (index >= list.size()) {
                      return false;
                  }
                  list.remove(index);
              } else {
                  int newIndex = list.size() + index;
                  
                  if (newIndex < 0) {
                      return false;
                  }
                  
                  list.remove(newIndex);
              }
              return true;
          }
  
          Class<?> clazz = currentObject.getClass();
          throw new JSONPathException("unsupported set operation." + clazz);
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      protected Collection<Object> getPropertyValues(final Object currentObject) {
          if (currentObject == null) {
              return null;
          }
  
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
  
          if (beanSerializer != null) {
              try {
                  return beanSerializer.getFieldValues(currentObject);
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path, e);
              }
          }
  
          if (currentObject instanceof Map) {
              Map map = (Map) currentObject;
              return map.values();
          }
  
          if (currentObject instanceof Collection) {
              return (Collection) currentObject;
          }
  
          throw new UnsupportedOperationException();
      }
  
      protected void deepGetObjects(final Object currentObject, List<Object> outValues) {
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
  
          Collection collection = null;
          if (beanSerializer != null) {
              try {
                  collection = beanSerializer.getFieldValues(currentObject);
                  outValues.add(currentObject);
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path, e);
              }
          } else if (currentObject instanceof Map) {
              outValues.add(currentObject);
              Map map = (Map) currentObject;
              collection = map.values();
          } else if (currentObject instanceof Collection) {
              collection = (Collection) currentObject;
          }
  
          if (collection != null) {
              for (Object fieldValue : collection) {
                  if (fieldValue == null || ParserConfig.isPrimitive2(fieldValue.getClass())) {
                      // skip
                  } else {
                      deepGetObjects(fieldValue, outValues);
                  }
              }
              return;
          }
  
          throw new UnsupportedOperationException(currentClass.getName());
      }
  
      protected void deepGetPropertyValues(final Object currentObject, List<Object> outValues) {
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
  
          Collection collection = null;
          if (beanSerializer != null) {
              try {
                  collection = beanSerializer.getFieldValues(currentObject);
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path, e);
              }
          } else if (currentObject instanceof Map) {
              Map map = (Map) currentObject;
              collection = map.values();
          } else if (currentObject instanceof Collection) {
              collection = (Collection) currentObject;
          }
  
          if (collection != null) {
              for (Object fieldValue : collection) {
                  if (fieldValue == null || ParserConfig.isPrimitive2(fieldValue.getClass())) {
                      outValues.add(fieldValue);
                  } else {
                      deepGetPropertyValues(fieldValue, outValues);
                  }
              }
              return;
          }
  
          throw new UnsupportedOperationException(currentClass.getName());
      }
  
      static boolean eq(Object a, Object b) {
          if (a == b) {
              return true;
          }
  
          if (a == null || b == null) {
              return false;
          }
  
          if (a.getClass() == b.getClass()) {
              return a.equals(b);
          }
  
          if (a instanceof Number) {
              if (b instanceof Number) {
                  return eqNotNull((Number) a, (Number) b);
              }
  
              return false;
          }
  
          return a.equals(b);
      }
  
      @SuppressWarnings("rawtypes")
      static boolean eqNotNull(Number a, Number b) {
          Class clazzA = a.getClass();
          boolean isIntA = isInt(clazzA);
  
          Class clazzB = b.getClass();
          boolean isIntB = isInt(clazzB);
          
          if (a instanceof BigDecimal) {
              BigDecimal decimalA = (BigDecimal) a;
              
              if (isIntB) {
                  return decimalA.equals(BigDecimal.valueOf(TypeUtils.longExtractValue(b)));
              }
          }
  
          if (isIntA) {
              if (isIntB) {
                  return a.longValue() == b.longValue();
              }
              
              if (b instanceof BigInteger) {
                  BigInteger bigIntB = (BigInteger) a;
                  BigInteger bigIntA = BigInteger.valueOf(a.longValue());
                  
                  return bigIntA.equals(bigIntB);
              }
          }
          
          if (isIntB) {
              if (a instanceof BigInteger) {
                  BigInteger bigIntA = (BigInteger) a;
                  BigInteger bigIntB = BigInteger.valueOf(TypeUtils.longExtractValue(b));
                  
                  return bigIntA.equals(bigIntB);
              }
          }
          
  
          boolean isDoubleA = isDouble(clazzA);
          boolean isDoubleB = isDouble(clazzB);
  
          if ((isDoubleA && isDoubleB) || (isDoubleA && isIntB) || (isDoubleB && isIntA)) {
              return a.doubleValue() == b.doubleValue();
          }
          
  
          return false;
      }
  
      protected static boolean isDouble(Class<?> clazzA) {
          return clazzA == Float.class || clazzA == Double.class;
      }
  
      protected static boolean isInt(Class<?> clazzA) {
          return clazzA == Byte.class || clazzA == Short.class || clazzA == Integer.class || clazzA == Long.class;
      }
  
      final static long SIZE = 0x4dea9618e618ae3cL; // TypeUtils.fnv1a_64("size");
      final static long LENGTH = 0xea11573f1af59eb5L; // TypeUtils.fnv1a_64("length");
  
      protected Object getPropertyValue(Object currentObject, String propertyName, long propertyNameHash) {
          if (currentObject == null) {
              return null;
          }
  
          if (currentObject instanceof String) {
              try {
                  JSONObject object = (JSONObject) JSON.parse((String) currentObject, parserConfig);
                  currentObject = object;
              } catch (Exception ex) {
                  // skip
              }
          }
  
          if (currentObject instanceof Map) {
              Map map = (Map) currentObject;
              Object val = map.get(propertyName);
  
              if (val == null && (SIZE == propertyNameHash || LENGTH == propertyNameHash)) {
                  val = map.size();
              }
  
              return val;
          }
  
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
          if (beanSerializer != null) {
              try {
                  return beanSerializer.getFieldValue(currentObject, propertyName, propertyNameHash, false);
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path + ", segement " + propertyName, e);
              }
          }
  
          if (currentObject instanceof List) {
              List list = (List) currentObject;
  
              if (SIZE == propertyNameHash || LENGTH == propertyNameHash) {
                  return list.size();
              }
  
              List<Object> fieldValues = null;
  
              for (int i = 0; i < list.size(); ++i) {
                  Object obj = list.get(i);
  
                  //
                  if (obj == list) {
                      if (fieldValues == null) {
                          fieldValues = new JSONArray(list.size());
                      }
                      fieldValues.add(obj);
                      continue;
                  }
  
                  Object itemValue = getPropertyValue(obj, propertyName, propertyNameHash);
                  if (itemValue instanceof Collection) {
                      Collection collection = (Collection) itemValue;
                      if (fieldValues == null) {
                          fieldValues = new JSONArray(list.size());
                      }
                      fieldValues.addAll(collection);
                  } else if (itemValue != null || !ignoreNullValue) {
                      if (fieldValues == null) {
                          fieldValues = new JSONArray(list.size());
                      }
                      fieldValues.add(itemValue);
                  }
              }
  
              if (fieldValues == null) {
                  fieldValues = Collections.emptyList();
              }
  
              return fieldValues;
          }
  
          if (currentObject instanceof Object[]) {
              Object[] array = (Object[]) currentObject;
  
              if (SIZE == propertyNameHash || LENGTH == propertyNameHash) {
                  return array.length;
              }
  
              List<Object> fieldValues = new JSONArray(array.length);
  
              for (int i = 0; i < array.length; ++i) {
                  Object obj = array[i];
  
                  //
                  if (obj == array) {
                      fieldValues.add(obj);
                      continue;
                  }
  
                  Object itemValue = getPropertyValue(obj, propertyName, propertyNameHash);
                  if (itemValue instanceof Collection) {
                      Collection collection = (Collection) itemValue;
                      fieldValues.addAll(collection);
                  } else if (itemValue != null || !ignoreNullValue) {
                      fieldValues.add(itemValue);
                  }
              }
  
              return fieldValues;
          }
  
          if (currentObject instanceof Enum) {
              final long NAME = 0xc4bcadba8e631b86L; // TypeUtils.fnv1a_64("name");
              final long ORDINAL = 0xf1ebc7c20322fc22L; //TypeUtils.fnv1a_64("ordinal");
  
              Enum e = (Enum) currentObject;
              if (NAME == propertyNameHash) {
                  return e.name();
              }
  
              if (ORDINAL == propertyNameHash) {
                  return e.ordinal();
              }
          }
  
          if (currentObject instanceof Calendar) {
              final long YEAR = 0x7c64634977425edcL; //TypeUtils.fnv1a_64("year");
              final long MONTH = 0xf4bdc3936faf56a5L; //TypeUtils.fnv1a_64("month");
              final long DAY = 0xca8d3918f4578f1dL; // TypeUtils.fnv1a_64("day");
              final long HOUR = 0x407efecc7eb5764fL; //TypeUtils.fnv1a_64("hour");
              final long MINUTE = 0x5bb2f9bdf2fad1e9L; //TypeUtils.fnv1a_64("minute");
              final long SECOND = 0xa49985ef4cee20bdL; //TypeUtils.fnv1a_64("second");
  
              Calendar e = (Calendar) currentObject;
              if (YEAR == propertyNameHash) {
                  return e.get(Calendar.YEAR);
              }
              if (MONTH == propertyNameHash) {
                  return e.get(Calendar.MONTH);
              }
              if (DAY == propertyNameHash) {
                  return e.get(Calendar.DAY_OF_MONTH);
              }
              if (HOUR == propertyNameHash) {
                  return e.get(Calendar.HOUR_OF_DAY);
              }
              if (MINUTE == propertyNameHash) {
                  return e.get(Calendar.MINUTE);
              }
              if (SECOND == propertyNameHash) {
                  return e.get(Calendar.SECOND);
              }
          }
  
          return null;
          //throw new JSONPathException("jsonpath error, path " + path + ", segement " + propertyName);
      }
      
      @SuppressWarnings("rawtypes")
      protected void deepScan(final Object currentObject, final String propertyName, List<Object> results) {
          if (currentObject == null) {
              return;
          }
  
          if (currentObject instanceof Map) {
              Map<?, ?> map = (Map<?, ?>) currentObject;
  
              for (Map.Entry entry : map.entrySet()) {
                  Object val = entry.getValue();
  
                  if (propertyName.equals(entry.getKey())) {
                      if (val instanceof Collection) {
                          results.addAll((Collection) val);
                      } else {
                          results.add(val);
                      }
                      continue;
                  }
  
                  if (val == null || ParserConfig.isPrimitive2(val.getClass())) {
                      continue;
                  }
  
                  deepScan(val, propertyName, results);
              }
  
              return;
          }
  
          if (currentObject instanceof Collection) {
              Iterator iterator = ((Collection) currentObject).iterator();
              while (iterator.hasNext()) {
                  Object next = iterator.next();
                  if (ParserConfig.isPrimitive2(next.getClass())) {
                      continue;
                  }
                  deepScan(next, propertyName, results);
              }
              return;
          }
  
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
          if (beanSerializer != null) {
              try {
                  FieldSerializer fieldDeser = beanSerializer.getFieldSerializer(propertyName);
                  if (fieldDeser != null) {
                      try {
                          Object val = fieldDeser.getPropertyValueDirect(currentObject);
                          results.add(val);
                      } catch (InvocationTargetException ex) {
                          throw new JSONException("getFieldValue error." + propertyName, ex);
                      } catch (IllegalAccessException ex) {
                          throw new JSONException("getFieldValue error." + propertyName, ex);
                      }
                      return;
                  }
                  List<Object> fieldValues = beanSerializer.getFieldValues(currentObject);
                  for (Object val : fieldValues) {
                      deepScan(val, propertyName, results);
                  }
                  return;
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path + ", segement " + propertyName, e);
              }
          }
  
          if (currentObject instanceof List) {
              List list = (List) currentObject;
  
              for (int i = 0; i < list.size(); ++i) {
                  Object val = list.get(i);
                  deepScan(val, propertyName, results);
              }
              return;
          }
      }
  
      protected void deepSet(final Object currentObject, final String propertyName, long propertyNameHash, Object value) {
          if (currentObject == null) {
              return;
          }
  
          if (currentObject instanceof Map) {
              Map map = (Map) currentObject;
  
              if (map.containsKey(propertyName)) {
                  Object val = map.get(propertyName);
                  map.put(propertyName, value);
                  return;
              }
  
              for (Object val : map.values()) {
                  deepSet(val, propertyName, propertyNameHash, value);
              }
              return;
          }
  
          final Class<?> currentClass = currentObject.getClass();
  
          JavaBeanDeserializer beanDeserializer = getJavaBeanDeserializer(currentClass);
          if (beanDeserializer != null) {
              try {
                  FieldDeserializer fieldDeser = beanDeserializer.getFieldDeserializer(propertyName);
                  if (fieldDeser != null) {
                      fieldDeser.setValue(currentObject, value);
                      return;
                  }
  
                  JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentClass);
                  List<Object> fieldValues = beanSerializer.getObjectFieldValues(currentObject);
                  for (Object val : fieldValues) {
                      deepSet(val, propertyName, propertyNameHash, value);
                  }
                  return;
              } catch (Exception e) {
                  throw new JSONPathException("jsonpath error, path " + path + ", segement " + propertyName, e);
              }
          }
  
          if (currentObject instanceof List) {
              List list = (List) currentObject;
  
              for (int i = 0; i < list.size(); ++i) {
                  Object val = list.get(i);
                  deepSet(val, propertyName, propertyNameHash, value);
              }
              return;
          }
      }
  
      @SuppressWarnings({ "unchecked", "rawtypes" })
      protected boolean setPropertyValue(Object parent, String name, long propertyNameHash, Object value) {
          if (parent instanceof Map) {
              ((Map) parent).put(name, value);
              return true;
          }
  
          if (parent instanceof List) {
              for (Object element : (List) parent) {
                  if (element == null) {
                      continue;
                  }
                  setPropertyValue(element, name, propertyNameHash, value);
              }
              return true;
          }
  
          ObjectDeserializer deserializer = parserConfig.getDeserializer(parent.getClass());
  
          JavaBeanDeserializer beanDeserializer = null;
          if (deserializer instanceof JavaBeanDeserializer) {
              beanDeserializer = (JavaBeanDeserializer) deserializer;
          }
  
          if (beanDeserializer != null) {
              FieldDeserializer fieldDeserializer = beanDeserializer.getFieldDeserializer(propertyNameHash);
              if (fieldDeserializer == null) {
                  return false;
              }
  
              if (value != null && value.getClass() != fieldDeserializer.fieldInfo.fieldClass) {
                  value = TypeUtils.cast(value, fieldDeserializer.fieldInfo.fieldType, parserConfig);
              }
  
              fieldDeserializer.setValue(parent, value);
              return true;
          }
  
          throw new UnsupportedOperationException();
      }
      
      @SuppressWarnings({"rawtypes" })
      protected boolean removePropertyValue(Object parent, String name, boolean deep) {
          if (parent instanceof Map) {
              Object origin = ((Map) parent).remove(name);
              boolean found = origin != null;
  
              if (deep) {
                  for (Object item : ((Map) parent).values()) {
                      removePropertyValue(item, name, deep);
                  }
              }
  
              return found;
          }
  
          ObjectDeserializer deserializer = parserConfig.getDeserializer(parent.getClass());
  
          JavaBeanDeserializer beanDeserializer = null;
          if (deserializer instanceof JavaBeanDeserializer) {
              beanDeserializer = (JavaBeanDeserializer) deserializer;
          }
  
          if (beanDeserializer != null) {
              FieldDeserializer fieldDeserializer = beanDeserializer.getFieldDeserializer(name);
  
              boolean found = false;
              if (fieldDeserializer != null) {
                  fieldDeserializer.setValue(parent, null);
                  found = true;
              }
  
              if (deep) {
                  Collection<Object> propertyValues = this.getPropertyValues(parent);
                  for (Object item : propertyValues) {
                      if (item == null) {
                          continue;
                      }
                      removePropertyValue(item, name, deep);
                  }
              }
  
              return found;
          }
  
          if (deep) {
              return false;
          }
  
          throw new UnsupportedOperationException();
      }
  
      protected JavaBeanSerializer getJavaBeanSerializer(final Class<?> currentClass) {
          JavaBeanSerializer beanSerializer = null;
          {
              ObjectSerializer serializer = serializeConfig.getObjectWriter(currentClass);
              if (serializer instanceof JavaBeanSerializer) {
                  beanSerializer = (JavaBeanSerializer) serializer;
              }
          }
          return beanSerializer;
      }
  
      protected JavaBeanDeserializer getJavaBeanDeserializer(final Class<?> currentClass) {
          JavaBeanDeserializer beanDeserializer = null;
          {
              ObjectDeserializer deserializer = parserConfig.getDeserializer(currentClass);
              if (deserializer instanceof JavaBeanDeserializer) {
                  beanDeserializer = (JavaBeanDeserializer) deserializer;
              }
          }
          return beanDeserializer;
      }
  
      @SuppressWarnings("rawtypes")
      int evalSize(Object currentObject) {
          if (currentObject == null) {
              return -1;
          }
  
          if (currentObject instanceof Collection) {
              return ((Collection) currentObject).size();
          }
  
          if (currentObject instanceof Object[]) {
              return ((Object[]) currentObject).length;
          }
  
          if (currentObject.getClass().isArray()) {
              return Array.getLength(currentObject);
          }
  
          if (currentObject instanceof Map) {
              int count = 0;
  
              for (Object value : ((Map) currentObject).values()) {
                  if (value != null) {
                      count++;
                  }
              }
              return count;
          }
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentObject.getClass());
  
          if (beanSerializer == null) {
              return -1;
          }
  
          try {
              return beanSerializer.getSize(currentObject);
          } catch (Exception e) {
              throw new JSONPathException("evalSize error : " + path, e);
          }
      }
  
      @SuppressWarnings({"rawtypes", "unchecked"})
      Set<?> evalKeySet(Object currentObject) {
          if (currentObject == null) {
              return null;
          }
  
          if (currentObject instanceof Map) {
              // For performance reasons return keySet directly, without filtering null-value key.
              return ((Map)currentObject).keySet();
          }
  
          if (currentObject instanceof Collection || currentObject instanceof Object[]
              || currentObject.getClass().isArray()) {
              return null;
          }
  
          JavaBeanSerializer beanSerializer = getJavaBeanSerializer(currentObject.getClass());
          if (beanSerializer == null) {
              return null;
          }
  
          try {
              return beanSerializer.getFieldNames(currentObject);
          } catch (Exception e) {
              throw new JSONPathException("evalKeySet error : " + path, e);
          }
      }
  
      public String toJSONString() {
          return JSON.toJSONString(path);
      }
  
      public static Object reserveToArray(Object object, String... paths) {
          JSONArray reserved = new JSONArray();
  
          if (paths == null || paths.length == 0) {
              return reserved;
          }
  
          for (String item : paths) {
              JSONPath path = JSONPath.compile(item);
              path.init();
  
              Object value = path.eval(object);
              reserved.add(value);
          }
  
          return reserved;
      }
  
      public static Object reserveToObject(Object object, String... paths) {
          if (paths == null || paths.length == 0) {
              return object;
          }
  
          JSONObject reserved = new JSONObject(true);
          for (String item : paths) {
              JSONPath path = JSONPath.compile(item);
              path.init();
              Segment lastSegement = path.segments[path.segments.length - 1];
              if (lastSegement instanceof PropertySegment) {
                  Object value = path.eval(object);
                  if (value == null) {
                      continue;
                  }
                  path.set(reserved, value);
              } else {
                  // skip
              }
          }
  
          return reserved;
      }
  }
  