Skip to content
Tomasz Fijałkowski edited this page Sep 24, 2016 · 14 revisions

epel expression language documentation

Contents

A Brief Example

When evaluating simple expressions, opel use only OpelEngine class and eval method. Remember that opel always return CompletableFuture.

String expression = "2 * 3 + 4";
OpelEngine engine = OpelEngineBuilder.create().build()
engine.eval(expression)
	.whenComplete((result, error) -> System.out.println(result));

this expression is transformed to equivalent code:

CompletableFuture.completedFuture(2)
    .thenCombine(CompletableFuture.completedFuture(3), (l, r) -> l * r)
    .thenCombine(CompletableFuture.completedFuture(4), (l, r) -> l + r)
    .whenComplete((result, error) -> System.out.println(result));

As you see, opel efficiently hides boilerplate of the CompletableFuture API.

Custom functions and values

Simle expressions like 2+2*2 are executed in the same thread in which the method eval was called. To taste the real power of opel asynchrony, you have to add a source of time-consuming operations.

Building custom function

To create custom function, OpelAsyncFunction<T> interface has to be implemented. It is an implementation of the well known adapter design pattern.

public class ExampleTemperatureFunction implements OpelAsyncFunction<BigDecimal> {
    @Override
    public CompletableFuture<BigDecimal> apply(List<CompletableFuture<?>> args) {
        return args.get(0).thenApply(city -> {
            // add code to call external service about temperature in given city
            return BigDecimal.valueOf(22);
        });
    }
}

Register functions in engine

When you build OpelEngine by OpelEngineBuilder you can simply add functions to it (how to implement own function is present in Building custom function section):

OpelEngine engine = OpelEngineBuilder.create()
	.withFunction("myFunctionName", new ExampleTemperatureFunction())
	.build()

Then myFunctionName is avalable in engine and can be call:

String expression = "myFunctionName('arg1', 'arg2') * 100";
engine.eval(expression)
	.whenComplete((result, error) -> System.out.println(result));

Register values in engine

In the same way as the functions you can register values using OpelEngineBuilder. You can add the completed values as well as CompletableFuture:

OpelEngine engine = OpelEngineBuilder.create()
	.withVariable("a", CompletableFuture.completedValue(2))
	.withCompletedVariable("b", 5)
	.build();

String expression = "a*a + b";

engine.eval(expression)
	.whenComplete((result, error) -> System.out.println(result));

Using eval context

In many sytuation functions results or values depend on some context. For example in wab application custom function returns information about signed in user depends on HTTP request. In opel the solution for that issue is EvalContext interface. You can provide your functions and values implementing your own EvalContext or you can build it using EvalContextBuilder - it is similar to register function and values in engine:

OpelEngine engine = OpelENgineBuilder.build();

EvalContext context = EvalContextBuilder.create()
	.withCompletedVariable('userName', 'Signed in uest name')
    .withFunction('userInfo', new UserInfoFunction()) // do not forget to create custom function UserInfoFunction
    .build();
    
String expression = "'Hello' + userName";

engine.eval(expression, context)
    .whenComplete((result, error) -> System.out.println(result));

If function or value is provided by both EvalContext and is registered in Engine, value from context override engine value - context has higher priority:

OpelEngine engine = OpelENgineBuilder
	.withCompletedVariable('userName', 'anonymous')
	.build();

EvalContext context = EvalContextBuilder.create()
	.withCompletedVariable('userName', 'Elvis Presley') //this value override value from engine
    .build();
    
String expression = "'Hello' + userName";

engine.eval(expression, context)
    .whenComplete((result, error) -> System.out.println(result)); // print 'Hello Elvis Presley'
Clone this wiki locally