JSONPathSegment parseFilter()

in core/src/main/java/com/alibaba/fastjson2/JSONPathParser.java [523:1094]


    JSONPathSegment parseFilter() {
        boolean parentheses = jsonReader.nextIfMatch('(');
        if (parentheses && filterNests > 0) {
            filterNests++;
        }

        boolean at = jsonReader.ch == '@';
        if (at) {
            jsonReader.next();
        } else if (jsonReader.nextIfMatchIdent('e', 'x', 'i', 's', 't', 's')) {
            if (!jsonReader.nextIfMatch('(')) {
                throw new JSONException(jsonReader.info("exists"));
            }

            if (jsonReader.nextIfMatch('@')) {
                if (jsonReader.nextIfMatch('.')) {
                    long hashCode = jsonReader.readFieldNameHashCodeUnquote();
                    String fieldName = jsonReader.getFieldName();

                    if (jsonReader.nextIfMatch(')')) {
                        if (parentheses) {
                            if (!jsonReader.nextIfMatch(')')) {
                                throw new JSONException(jsonReader.info("jsonpath syntax error"));
                            }
                        }
                        return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
                    } else if (jsonReader.ch == '.') {
                        List<String> names = new ArrayList<>();
                        names.add(fieldName);
                        do {
                            jsonReader.next();
                            fieldName = jsonReader.readFieldNameUnquote();
                            names.add(fieldName);
                        } while (jsonReader.ch == '.');

                        if (jsonReader.nextIfMatch(')')) {
                            if (parentheses) {
                                if (!jsonReader.nextIfMatch(')')) {
                                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                                }
                            }
                        }

                        return new JSONPathFilter.NamesExistsFilter(names);
                    }
                }
            }

            throw new JSONException(jsonReader.info("jsonpath syntax error"));
        }

        boolean starts = jsonReader.nextIfMatchIdent('s', 't', 'a', 'r', 't', 's');
        boolean ends = (!starts) && jsonReader.nextIfMatchIdent('e', 'n', 'd', 's');
        if ((at && (starts || ends))
                || (jsonReader.ch != '.'
                && jsonReader.ch != '['
                && !isOperator(jsonReader.ch)
                && !JSONReader.isFirstIdentifier(jsonReader.ch))
        ) {
            if (jsonReader.nextIfMatch('(')) {
                filterNests++;
                filterNests++;
                return parseFilter();
            }
            if (!at) {
                throw new JSONException(jsonReader.info("jsonpath syntax error"));
            }

            JSONPathFilter.Operator operator;
            if (starts || ends) {
                jsonReader.readFieldNameHashCodeUnquote();
                String fieldName = jsonReader.getFieldName();
                if (!"with".equalsIgnoreCase(fieldName)) {
                    throw new JSONException("not support operator : " + fieldName);
                }
                operator = starts ? JSONPathFilter.Operator.STARTS_WITH : JSONPathFilter.Operator.ENDS_WITH;
            } else {
                operator = JSONPath.parseOperator(jsonReader);
            }

            JSONPathSegment segment = null;
            if (jsonReader.isNumber()) {
                Number number = jsonReader.readNumber();
                if (number instanceof Integer || number instanceof Long) {
                    segment = new JSONPathFilter.NameIntOpSegment(null, 0, null, null, null, operator, number.longValue());
                }
            } else if (jsonReader.isString()) {
                String string = jsonReader.readString();

                switch (operator) {
                    case STARTS_WITH:
                        segment = new JSONPathFilter.StartsWithSegment(null, 0, string);
                        break;
                    case ENDS_WITH:
                        segment = new JSONPathFilter.EndsWithSegment(null, 0, string);
                        break;
                    default:
                        throw new JSONException("syntax error, " + string);
                }
            }

            while (jsonReader.ch == '&' || jsonReader.ch == '|') {
                filterNests--;
                segment = parseFilterRest(segment);
            }

            if (segment != null) {
                if (parentheses) {
                    if (!jsonReader.nextIfMatch(')')) {
                        throw new JSONException(jsonReader.info("jsonpath syntax error"));
                    }
                }
                return segment;
            }

            throw new JSONException(jsonReader.info("jsonpath syntax error"));
        }

        String fieldName = null;
        long hashCode = 0;
        if (at) {
            if (jsonReader.ch == '[') {
                JSONPathSegment segment = parseArrayAccess();
                if (segment instanceof JSONPathSegmentName) {
                    fieldName = ((JSONPathSegmentName) segment).name;
                    hashCode = ((JSONPathSegmentName) segment).nameHashCode;
                } else {
                    JSONPathFilter.Operator operator = JSONPath.parseOperator(jsonReader);
                    if (jsonReader.ch == '@') {
                        JSONPathSegment segment2 = parseSegment();
                        if (parentheses) {
                            jsonReader.nextIfMatch(')');
                        }
                        return new JSONPathFilter.Segment2Filter(segment, operator, segment2);
                    } else {
                        Object value = jsonReader.readAny();
                        if (parentheses) {
                            jsonReader.nextIfMatch(')');
                        }
                        if (segment instanceof JSONPathSegment.RangeIndexSegment) {
                            return new JSONPathFilter.RangeIndexSegmentFilter((JSONPathSegment.RangeIndexSegment) segment, operator, value);
                        }
                        return new JSONPathFilter.SegmentFilter(segment, operator, value);
                    }
                }
            } else if (isOperator(jsonReader.ch)) {
                JSONPathSegment.SelfSegment self = JSONPathSegment.SelfSegment.INSTANCE;
                JSONPathFilter.Operator operator = JSONPath.parseOperator(jsonReader);
                if (jsonReader.ch == '@') {
                    JSONPathSegment segment2 = parseSegment();
                    if (parentheses) {
                        jsonReader.nextIfMatch(')');
                    }
                    return new JSONPathFilter.Segment2Filter(self, operator, segment2);
                } else {
                    Object value = jsonReader.readAny();
                    JSONPathSegment segment = new JSONPathFilter.SegmentFilter(self, operator, value);
                    if (parentheses) {
                        while (jsonReader.ch == '&' || jsonReader.ch == '|' || jsonReader.ch == 'a' || jsonReader.ch == 'o') {
                            filterNests--;
                            segment = parseFilterRest(segment);
                        }
                        jsonReader.nextIfMatch(')');
                    }
                    return segment;
                }
            } else {
                jsonReader.next();
            }
        }

        if (fieldName == null) {
            hashCode = jsonReader.readFieldNameHashCodeUnquote();
            fieldName = jsonReader.getFieldName();
        }

        if (parentheses) {
            if (jsonReader.nextIfMatch(')')) {
                if (filterNests > 0) {
                    filterNests--;
                }
                return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
            }
        }

        String functionName = null;

        long[] hashCode2 = null;
        String[] fieldName2 = null;
        while (jsonReader.ch == '.') {
            jsonReader.next();
            long hash = jsonReader.readFieldNameHashCodeUnquote();
            String str = jsonReader.getFieldName();

            if (jsonReader.ch == '(') {
                functionName = str;
                break;
            }

            if (hashCode2 == null) {
                hashCode2 = new long[]{hash};
                fieldName2 = new String[]{str};
            } else {
                hashCode2 = Arrays.copyOf(hashCode2, hashCode2.length + 1);
                hashCode2[hashCode2.length - 1] = hash;
                fieldName2 = Arrays.copyOf(fieldName2, fieldName2.length + 1);
                fieldName2[fieldName2.length - 1] = str;
            }
        }

        if (fieldName2 == null && !parentheses
                && (jsonReader.ch == ']' || jsonReader.ch == '|' || jsonReader.ch == '&')
        ) {
            return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
        }

        JSONPathFilter.Operator operator = null;
        Function function = null;
        if (jsonReader.ch == '(') {
            if (functionName == null) {
                functionName = fieldName;
                fieldName = null;
            }

            switch (functionName) {
                case "type":
                    hashCode = 0;
                    function = JSONPathFunction.TypeFunction.INSTANCE;
                    break;
                case "size":
                    hashCode = 0;
                    function = JSONPathFunction.SizeFunction.INSTANCE;
                    break;
                case "contains":
                    hashCode = 0;
                    operator = JSONPathFilter.Operator.CONTAINS;
                    break;
                default:
                    throw new JSONException("syntax error, function not support " + fieldName);
            }

            if (function != null) {
                jsonReader.next();
                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException("syntax error, function " + functionName);
                }
            }
        }

        if (function == null && jsonReader.ch == '[') {
            jsonReader.next();
            if (jsonReader.ch == '?') {
                jsonReader.next();
                JSONPathFilter subFilter = (JSONPathFilter) parseFilter();
                function = new JSONPathFunction.FilterFunction(subFilter);
            } else {
                int index = jsonReader.readInt32Value();
                function = new JSONPathFunction.IndexValue(index);
            }
            if (!jsonReader.nextIfMatch(']')) {
                throw new JSONException("syntax error");
            }
        }
        if (operator == null) {
            if (parentheses && jsonReader.nextIfMatch(')')) {
                return new JSONPathFilter.NameExistsFilter(fieldName, hashCode);
            }
            operator = JSONPath.parseOperator(jsonReader);
        }

        switch (operator) {
            case REG_MATCH:
            case RLIKE:
            case NOT_RLIKE: {
                String regex;
                boolean ignoreCase;
                if (jsonReader.isString()) {
                    regex = jsonReader.readString();
                    ignoreCase = false;
                } else {
                    regex = jsonReader.readPattern();
                    ignoreCase = jsonReader.nextIfMatch('i');
                }

                Pattern pattern = ignoreCase
                        ? Pattern.compile(regex, Pattern.CASE_INSENSITIVE)
                        : Pattern.compile(regex);

                JSONPathSegment segment = new JSONPathFilter.NameRLikeSegment(fieldName, hashCode, pattern, operator == JSONPathFilter.Operator.NOT_RLIKE);
                if (jsonReader.ch == '&' || jsonReader.ch == '|' || jsonReader.ch == 'a' || jsonReader.ch == 'o') {
                    filterNests--;
                    segment = parseFilterRest(segment);
                }
                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                return segment;
            }
            case IN:
            case NOT_IN: {
                if (jsonReader.ch != '(') {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                jsonReader.next();

                JSONPathSegment segment;
                if (jsonReader.isString()) {
                    List<String> list = new ArrayList<>();
                    while (jsonReader.isString()) {
                        list.add(jsonReader.readString());
                    }
                    String[] strArray = new String[list.size()];
                    list.toArray(strArray);
                    segment = new JSONPathFilter.NameStringInSegment(
                            fieldName,
                            hashCode,
                            strArray,
                            operator == JSONPathFilter.Operator.NOT_IN
                    );
                } else if (jsonReader.isNumber()) {
                    List<Number> list = new ArrayList<>();
                    while (jsonReader.isNumber()) {
                        list.add(jsonReader.readNumber());
                    }
                    long[] values = new long[list.size()];
                    for (int i = 0; i < list.size(); i++) {
                        values[i] = list.get(i).longValue();
                    }
                    segment = new JSONPathFilter.NameIntInSegment(fieldName, hashCode, fieldName2, hashCode2, function, values, operator == JSONPathFilter.Operator.NOT_IN);
                } else {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }

                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                if (jsonReader.ch == '&' || jsonReader.ch == '|' || jsonReader.ch == 'a' || jsonReader.ch == 'o') {
                    filterNests--;
                    segment = parseFilterRest(segment);
                }
                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }

                return segment;
            }
            case CONTAINS: {
                if (jsonReader.ch != '(') {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                jsonReader.next();

                JSONPathSegment segment;
                if (jsonReader.isString()) {
                    List<String> list = new ArrayList<>();
                    while (jsonReader.isString()) {
                        list.add(jsonReader.readString());
                    }
                    String[] strArray = new String[list.size()];
                    list.toArray(strArray);
                    segment = new JSONPathFilter.NameStringContainsSegment(
                            fieldName,
                            hashCode,
                            fieldName2,
                            hashCode2,
                            strArray,
                            false
                    );
                } else if (jsonReader.isNumber()) {
                    List<Number> list = new ArrayList<>();
                    while (jsonReader.isNumber()) {
                        list.add(jsonReader.readNumber());
                    }
                    long[] values = new long[list.size()];
                    for (int i = 0; i < list.size(); i++) {
                        values[i] = list.get(i).longValue();
                    }
                    segment = new JSONPathFilter.NameLongContainsSegment(
                            fieldName,
                            hashCode,
                            fieldName2,
                            hashCode2,
                            values,
                            false
                    );
                } else {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }

                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                if (!jsonReader.nextIfMatch(')')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }

                return segment;
            }
            case BETWEEN:
            case NOT_BETWEEN: {
                JSONPathSegment segment;
                if (jsonReader.isNumber()) {
                    Number begin = jsonReader.readNumber();
                    String and = jsonReader.readFieldNameUnquote();
                    if (!"and".equalsIgnoreCase(and)) {
                        throw new JSONException("syntax error, " + and);
                    }
                    Number end = jsonReader.readNumber();
                    segment = new JSONPathFilter.NameIntBetweenSegment(fieldName, hashCode, begin.longValue(), end.longValue(), operator == JSONPathFilter.Operator.NOT_BETWEEN);
                } else {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }

                if (parentheses) {
                    if (!jsonReader.nextIfMatch(')')) {
                        throw new JSONException(jsonReader.info("jsonpath syntax error"));
                    }
                }

                return segment;
            }
            default:
                break;
        }

        JSONPathSegment segment = null;
        switch (jsonReader.ch) {
            case '-':
            case '+':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9': {
                Number number = jsonReader.readNumber();
                if (number instanceof Integer || number instanceof Long) {
                    segment = new JSONPathFilter.NameIntOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, number.longValue());
                } else if (number instanceof BigDecimal) {
                    segment = new JSONPathFilter.NameDecimalOpSegment(fieldName, hashCode, operator, (BigDecimal) number);
                } else if (number instanceof Float || number instanceof Double) {
                    segment = new JSONPathFilter.NameDoubleOpSegment(fieldName, hashCode, operator, number.doubleValue());
                } else {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                break;
            }
            case '"':
            case '\'': {
                String strVal = jsonReader.readString();
                int p0 = strVal.indexOf('%');
                if (p0 == -1) {
                    if (operator == JSONPathFilter.Operator.LIKE) {
                        operator = JSONPathFilter.Operator.EQ;
                    } else if (operator == JSONPathFilter.Operator.NOT_LIKE) {
                        operator = JSONPathFilter.Operator.NE;
                    }
                }

                if (operator == JSONPathFilter.Operator.LIKE || operator == JSONPathFilter.Operator.NOT_LIKE) {
                    String[] items = strVal.split("%");

                    String startsWithValue = null;
                    String endsWithValue = null;
                    String[] containsValues = null;
                    if (p0 == 0) {
                        if (strVal.charAt(strVal.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 (strVal.charAt(strVal.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);
                        }
                    }
                    segment = new JSONPathFilter.NameMatchFilter(
                            fieldName,
                            hashCode,
                            startsWithValue,
                            endsWithValue,
                            containsValues,
                            operator == JSONPathFilter.Operator.NOT_LIKE
                    );
                } else {
                    segment = new JSONPathFilter.NameStringOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, strVal);
                }
                break;
            }
            case 't': {
                String ident = jsonReader.readFieldNameUnquote();
                if ("true".equalsIgnoreCase(ident)) {
                    segment = new JSONPathFilter.NameIntOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, 1);
                    break;
                }
                break;
            }
            case 'f': {
                String ident = jsonReader.readFieldNameUnquote();
                if ("false".equalsIgnoreCase(ident)) {
                    segment = new JSONPathFilter.NameIntOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, 0);
                    break;
                }
                break;
            }
            case '[': {
                JSONArray array = jsonReader.read(JSONArray.class);
                segment = new JSONPathFilter.NameArrayOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, array);
                break;
            }
            case '{': {
                JSONObject object = jsonReader.read(JSONObject.class);
                segment = new JSONPathFilter.NameObjectOpSegment(fieldName, hashCode, fieldName2, hashCode2, function, operator, object);
                break;
            }
            case 'n':
                boolean nextNull = jsonReader.nextIfNull();
                if (nextNull) {
                    segment = new JSONPathFilter.NameIsNull(fieldName, hashCode, fieldName2, hashCode2, function);
                    break;
                }
                throw new JSONException(jsonReader.info("jsonpath syntax error"));
            case '@':
                jsonReader.next();
                if (!jsonReader.nextIfMatch('.')) {
                    throw new JSONException(jsonReader.info("jsonpath syntax error"));
                }
                String fieldName1 = jsonReader.readFieldNameUnquote();
                long fieldName1Hash = Fnv.hashCode64(fieldName1);

                segment = new JSONPathFilter.NameName(fieldName, hashCode, fieldName1, fieldName1Hash);
                break;
            default:
                throw new JSONException(jsonReader.info("jsonpath syntax error"));
        }

        if (jsonReader.ch == '&' || jsonReader.ch == '|' || jsonReader.ch == 'a' || jsonReader.ch == 'o') {
            filterNests--;
            segment = parseFilterRest(segment);
        }

        if (parentheses) {
            if (!jsonReader.nextIfMatch(')')) {
                throw new JSONException(jsonReader.info("jsonpath syntax error"));
            }
        }

        return segment;
    }