首页 > 解决方案 > 查询解析器构建 Predicate 以过滤内存中的 bean

问题描述

我正在寻找一个可以解释查询(例如在配置文件中定义)并从中构造一个 java.util.Predicate 的解析器,我可以用它来过滤一组 bean。

例如,我希望能够定义

name = "John*" AND age < 30 and address.city = "Frankfurt"

然后我想使用这个谓词来过滤类型的bean

class Person {
    String getName() { ... }
    int getAge() { ... }
    Address getAddress() { ... }
}
class Address {
    String getCity() { ... }
}

实际上它并不真的需要使用 Predicate 实例,我只是在寻找一个工具,它接受我的文本查询和 bean 集合并返回这些 bean 的过滤集合。

标签: javaconfigurationpredicate

解决方案


你可以使用javax.script.ScriptEngine

public static void main(String[] args) {
    try {
        ScriptEngineManager factory = new ScriptEngineManager();
        // create a JavaScript engine
        ScriptEngine engine = factory.getEngineByName("JavaScript");
        // evaluate JavaScript code from String
        String expression
                = "name == \"John\" && age < 30 && address.city == \"Frankfurt\"";
        Person person = new Person("John", 25, new Address("Frankfurt"));
        System.out.println(engine.eval(expression, new BeanBindings(person)));
    } catch (ScriptException ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
    }
}
public static class Person {
    String name;
    int age;
    Address address;

    Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public Address getAddress() {
        return address;
    }
}
public static class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }
    public String getCity() {
        return city;
    }
}

private static class BeanBindings implements Bindings {
    private final Object bean;

    public BeanBindings(Object bean) {
        this.bean = bean;
    }

    @Override
    public Object put(String name, Object value) {
        return null;
    }

    @Override
    public void putAll(Map<? extends String, ? extends Object> toMerge) {
    }

    @Override
    public boolean containsKey(Object key) {
        Method getter = getter((String)key);
        return getter != null;
    }

    @Override
    public Object get(Object key) {
        Method getter = getter((String)key);
        try {
            return getter == null ? null : getter.invoke(bean);
        } catch (IllegalAccessException | IllegalArgumentException
                | InvocationTargetException ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    @Override
    public Object remove(Object key) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public int size() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean isEmpty() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean containsValue(Object value) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public Set<String> keySet() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public Collection<Object> values() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private Method getter(String propName) {
        if (propName == null || propName.length() == 0) {
            throw new IllegalArgumentException("empty property name");
        }
        try {
            String sfx = Character.toUpperCase(propName.charAt(0))
                    + propName.substring(1);
            return bean.getClass().getMethod(
                    "get" + sfx, new Class[0]);
        } catch (NoSuchMethodException ex) {
            return null;
        } catch (SecurityException ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }
}

推荐阅读