Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construct a predicate using custom object model in java

I have a object model like the one given below

public class Filter {
    public String field;
    public ConditionalOperator operator;
    public String value;
}

I have a list of objects like List<Employees>

Based on the Filter inputs, I want to construct the predicate on the property and apply that to the list of employees.

Example:

Employees
   FirstName
   LastName
   CreatedOn (Timestamp)
   Status (FullTime/ parttime)
   IsActive(True / False)

Filter conditions will be looking like 

[
{ "field":"FirstName", "operator":"StartsWith", "value":"john"}
]

The operators are like

Contains, StartsWith, EndsWith,Equals

I would like to construct the predicate like PredicateBuilder(fieldName, operator, value) so that I can get like

Predicate<Employees> filter = e -> e.FirstName.StartsWith("john");

I have tried the one link

Predicate with Reflection in java

In this, I was able to infer the propertyname, apply the equals method to the dynamic value like

Class<?> cls = Employees.class;
Class<?> noparams[] = {};
try {
    Method method = cls.getDeclaredMethod("get" + filter.getField(), noparams);
    Predicate<ExecutionReport> first = e -> {
        try {
            Object val = method.invoke(e);
            System.out.println(val);
            return method.invoke(e).toString().startsWith(filter.getField());
        } catch (IllegalAccessException illegalAccessException) {
            illegalAccessException.printStackTrace();
        } catch (InvocationTargetException invocationTargetException) {
            invocationTargetException.printStackTrace();
        }
        return false;
    };
    return first;
} catch (NoSuchMethodException e) {
    e.printStackTrace();
}

Please guide me on how to construct the dynamic predicates, I have been searching the internet but with no luck and i have less information on reflection and predicates in Java

like image 346
Saravanan Avatar asked Jan 26 '23 03:01

Saravanan


1 Answers

Let's start by using some simple building blocks:

public enum ConditionalOperator implements BiPredicate<String, String> {
    Contains((test, value) -> test.contains(value)),
    StartsWith((test, value) -> test.startsWith(value)),
    EndsWith((test, value) -> test.endsWith(value)),
    Equals((test, value) -> test.equals(value));

    private final BiPredicate<String, String> predicate;

    ConditionalOperator(BiPredicate<String, String> predicate) {
        this.predicate = predicate;
    }

    @Override
    public boolean test(String test, String value) {
        return predicate.test(test, value);
    }
}

I took the liberty to implement it as an enum, not sure what it is in your design.

Now we need a value extractor:

public static Function<Employee, String> getField(String name) {
    try {
        Method method = Employee.class.getMethod("get" + name);
        if (method.getReturnType() != String.class) {
            throw new IllegalArgumentException("Employee.get" + name + " does not return a String");
        }
        return e -> {
            try {
                return (String) method.invoke(e);
            } catch (ReflectiveOperationException roe) {
                // Unlikely to happen
                throw new RuntimeException(roe);
            }
        }
    } catch (ReflectiveOperationException roe) {
        // getter does not exist, what now?
        throw new IllegalArgumentException(roe);
    }
}

And last, we need to chain everything together:

public static Predicate<Employee> buildPredicate(Filter f) {
    Function<Employee, String> fieldGetter = getField(f.field());
    ConditionalOperator op = f.operator();
    String value = f.value();
    return e -> op.test(fieldGetter.apply(e), value);
}

This only works for Strings for now, but you can probably adapt it - the easiest is to remove the the check for the return value and instead of casting the result to String call .toString() on it.

like image 164
Johannes Kuhn Avatar answered Jan 27 '23 16:01

Johannes Kuhn