Executando expressões lógicas dinâmicas em java

Esses dias me deparei com o seguinte problema: como gerar expressões lógicas a partir de atributos anotados de um objeto como no exemplo abaixo, que são construídas pelo usuário através de uma interface gráfica?

public class Contact {

  @MyAnnotion
  private String name;

  @MyAnnotation(operators={Operator.Equal, Operator.GreaterThan....})
  private Date birthday;

  @MyAnnotion 
  private String address;
 
  @MyAnnotation(operators={Operator.Equal, Operator.GreaterThan....})
  private Integer age;

}

Digamos que queremos construir regras como “nome contains ‘Joao’ AND birthday <= '10/12/2013' OR age < 18", ou seja queremos criar regras para serem aplicadas nos objetos Contacts e selecionar os objetos de acordo com aqueles que atendem a regra ou não. Construir a regra em si não é um grande problema. O desafio está em executar a regra construída obedecendo a precedência dos operadores lógicos, e havendo parênteses, aumenta a complexidade, pois temos que executar os mais internos primeiro.

Uma das soluções, apontada por um amigo meu, é gerar um programa java script e utilizar a engine de script para avaliar a expressão lógica gerada. Por exemplo a expressão “nome igual ‘Eduardo’ and idade > 10” seria traduzido para algo assim:

if('Eduardo' == 'Eduardo' && 30 > 10){ return true; } else { return false; }

Basicamente geramos uma string contendo um código semelhante ao mostrado acima substituindo o campos (nome,idade no exemplo) pelos seus respectivos valores e os operadores pelos seus correspondentes na linguagem javascript.

Abaixo segue o código completo do programa em java, mostrando a solução que adotamos e que funcionou muito bem, muito superior ao que tínhamos antes que gerava um SQL, mas tinha um problema grave, pois quando estamos criando ou editando um objeto ainda não colocamos o objeto (Contact no caso desse artigo) no banco, logo as regras que estão lá não conseguirão ser aplicadas sobre os novos dados. Mas salvar o objeto no banco para poder aplicar as regras e apagá-lo caso, o usuário desista da edição ou criação do novo contato, é um convite para erros de inconsistência no banco. O jeito foi executar as regras sem precisar do banco e essa solução de usar a script engine da JVM caiu como uma luva.

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ExecutaRegra {
	
	public static void main(String[] args){
		try {
			String expressaoJavascript = "if('Teste'=='Teste1' || 1==0){r=true} else {r=false}";

			ScriptEngineManager mgr = new ScriptEngineManager();
			ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
			boolean result = (Boolean) jsEngine.eval(expressaoJavascript);
			
			System.out.println("Resultado: " + result);
			
		} catch (ScriptException ex) {
			ex.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} 
	}
}