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;
}