public static List parseJsonPath()

in processing/src/main/java/org/apache/druid/segment/nested/NestedPathFinder.java [109:199]


  public static List<NestedPathPart> parseJsonPath(@Nullable String path)
  {
    if (path == null || path.isEmpty()) {
      return Collections.emptyList();
    }
    List<NestedPathPart> parts = new ArrayList<>();

    if (!path.startsWith(JSON_PATH_ROOT)) {
      badFormatJsonPath(path, "it must start with '$'");
    }

    if (path.length() == 1) {
      return Collections.emptyList();
    }

    int partMark = -1;  // position to start the next substring to build the path part
    int dotMark = -1;   // last position where a '.' character was encountered, indicating the start of a new part
    int arrayMark = -1; // position of leading '[' indicating start of array (or field name if ' immediately follows)
    int quoteMark = -1; // position of leading ', indicating a quoted field name

    // start at position 1 since $ is special
    for (int i = 1; i < path.length(); i++) {
      final char current = path.charAt(i);
      if (current == '.' && arrayMark < 0 && quoteMark < 0) {
        if (dotMark >= 0) {
          parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
        }
        dotMark = i;
        partMark = i + 1;
      } else if (current == '[' && arrayMark < 0 && quoteMark < 0) {
        if (dotMark == (i - 1) && dotMark != 0) {
          badFormatJsonPath(path, "found '[' at invalid position [%s], must not follow '.' or must be contained with '", i);
        }
        if (dotMark >= 0 && i > 1) {
          parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
          dotMark = -1;
        }
        arrayMark = i;
        partMark = i + 1;
      } else if (current == ']' && arrayMark >= 0 && quoteMark < 0) {
        String maybeNumber = getPathSubstring(path, partMark, i);
        try {
          int index = Integer.parseInt(maybeNumber);
          parts.add(new NestedPathArrayElement(index));
          dotMark = -1;
          arrayMark = -1;
          partMark = i + 1;
        }
        catch (NumberFormatException ignored) {
          badFormatJsonPath(path, "array specifier [%s] should be a number, it was not.  Use ' if this value was meant to be a field name", maybeNumber);
        }
      } else if (dotMark == -1 && arrayMark == -1) {
        badFormatJsonPath(path, "path parts must be separated with '.'");
      } else if (current == '\'' && quoteMark < 0) {
        if (arrayMark != i - 1) {
          badFormatJsonPath(path, "single-quote (') must be immediately after '['");
        }
        quoteMark = i;
        partMark = i + 1;
      } else if (current == '\'' && quoteMark >= 0 && path.charAt(i - 1) != '\\') {
        if (path.charAt(i + 1) != ']') {
          if (arrayMark >= 0) {
            continue;
          }
          badFormatJsonPath(path, "closing single-quote (') must immediately precede ']'");
        }

        parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
        dotMark = -1;
        quoteMark = -1;
        // chomp to next char to eat closing array
        if (++i == path.length()) {
          break;
        }
        partMark = i + 1;
        arrayMark = -1;
      }
    }
    // add the last element, this should never be an array because they close themselves
    if (partMark < path.length()) {
      if (quoteMark != -1) {
        badFormatJsonPath(path, "unterminated single-quote (')");
      }
      if (arrayMark != -1) {
        badFormatJsonPath(path, "unterminated '['");
      }
      parts.add(new NestedPathField(path.substring(partMark)));
    }

    return parts;
  }