Assuming your Cat class follows JavaBean convention you could use java.beans.PropertyDescriptor to access getter Method based on property name.
This allows us to learn what type of value we are dealing with. If it is numeric we can handle greater_than and other operators, but if it is non-numeric we should handle only equals_to operator.
"Simplified" and very limited solution could look like:
NOTE:
- Solution doesn't support primitive numeric types like int. Use Integer, Double etc. instead.
- I am converting all numbers to BigDecimal and use compareTo to simplify numerical type comparison, if you get any bugs for big numbers or very precise ones feel free to replace it with proper type comparison).
- for equality check it compares string representation of objects (result of toString()), so for Color you can't use BLUE but your JSON would need to hold java.awt.Color[r=0,g=0,b=255])
class PredicatesUtil {
static <T> Predicate<T> filters(Class<T> clazz, String filtersJson) {
JSONObject jsonObject = new JSONObject(filtersJson);
List<Predicate<T>> predicateList = new ArrayList<>();
for (String property : jsonObject.keySet()) {
JSONObject filterSettings = jsonObject.getJSONObject(property);
try {
String operator = filterSettings.getString("operator");
String value = filterSettings.getString("value");
predicateList.add(propertyPredicate(clazz, property, operator, value));
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
}
return combinePredicatesUsingAND(predicateList);
}
static <T> Predicate<T> combinePredicatesUsingAND(List<Predicate<T>> predicateList) {
return t -> {
for (Predicate<T> pr : predicateList) {
if (!pr.test(t))
return false;
}
return true;
};
}
static <T> Predicate<T> propertyPredicate(Class<T> clazz, String property,
String operator, String value)
throws IntrospectionException {
final Method m = new PropertyDescriptor(property, clazz).getReadMethod();
final Class<?> returnType = m.getReturnType();
return obj -> {
try {
Object getterValue = m.invoke(obj);
if (Number.class.isAssignableFrom(returnType)) {
BigDecimal getValue = new BigDecimal(getterValue.toString());
BigDecimal numValue = new BigDecimal(value);
int compared = getValue.compareTo(numValue);
if (operator.equalsIgnoreCase("equal_to")) {
return compared == 0;
} else if (operator.equalsIgnoreCase("lesser_than")) {
return compared < 0;
} else if (operator.equalsIgnoreCase("greater_than")) {
return compared > 0;
} else {
throw new RuntimeException("not recognized operator for numeric type: " + operator);
}
} else {
//System.out.println("testing non-numeric, only euals_to");
if (operator.equalsIgnoreCase("equal_to")) {
return value.equalsIgnoreCase(getterValue.toString());
}
throw new RuntimeException("not recognized operator: " + operator);
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
}
which can be used like:
class Cat {
private Color eyeColour;
private Integer furLength;
Cat(Color eyeColor, Integer furLength) {
this.eyeColour = eyeColor;
this.furLength = furLength;
}
public Color getEyeColour() {
return eyeColour;
}
public Integer getFurLength() {
return furLength;
}
public void setEyeColour(Color eyeColour) {
this.eyeColour = eyeColour;
}
public void setFurLength(Integer furLength) {
this.furLength = furLength;
}
@Override
public String toString() {
return "Cat{" +
"eyeColor=" + eyeColour +
", furLength=" + furLength +
'}';
}
}
class CatsDemo {
public static void main(String[] args) {
String json =
"{\n" +
" eyeColour:{\n" +
" operator: \"equal_to\",\n" +
" value: \"java.awt.Color[r=0,g=0,b=255]\"\n" +
" },\n" +
" furLength: {\n" +
" operator: \"greater_than\",\n" +
" value: \"3\"\n" +
" }\n" +
"}";
List<Cat> cats = List.of(
new Cat(Color.blue, 1),
new Cat(Color.blue, 2),
new Cat(Color.blue, 3),
new Cat(Color.blue, 4),
new Cat(Color.blue, 5),
new Cat(Color.yellow, 1),
new Cat(Color.yellow, 2),
new Cat(Color.yellow, 3),
new Cat(Color.yellow, 4),
new Cat(Color.yellow, 5)
);
cats.stream()
.filter(PredicatesUtil.filters(Cat.class, json))
.forEach(System.out::println);
}
}
Output:
Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=4}
Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=5}
Predicateand generalize the statement to usecats.stream().filter(predicateAsInput).collect(Collectors.toList());Predicate<Cat>from that by extracting the actual value using reflection to create aFunction <Cat, ValueType>and then apply the operator to see if the value matches.