Convert Java Lambda predicates

I am trying to perform a model transformation using Java lambda. Below follows a block of code that exemplifies what I want to do:

public String translatePrimitivePredicate(){
    Predicate<Integer> predicate = a -> a == 1;     
    return translate(predicate) //"{$varA} === 1";
}

So, I want to retrieve each term from the predicate and use it to execute a business logic. Using the example above, the conversion would be executed in such a way that the term' a ' would be translated to a java String "{varA stick}", the logical operator '= = 'to another java String" = = = " and the value '1' to "1". Thus, the return of the translatePrimitivePredicate method would be the concatenation of the terms, such as, "{varA stick} === 1".

How can I do this?

Author: Victor Stafusa, 2017-12-27

1 answers

Picking up a lambda expression for runtime translation is a virtually impossible task (so far), I'll try to give you two suggestions:

1 - think of a simpler solution that does not involve a translation of the way you described;

2 - if you really need to do this way, I believe that lambda-from-string can help you with a slightly different solution(Function instead of Predicate).

I tried to simplify as much as I could, but even then I still think it is not a very interesting solution for production(mainly due to performance).

Solution

Instead of trying to capture the lambda expression at runtime we can perform a eval of an expression(String) by applying the translation and converting the String into a function .

Pom.xml

<dependency>
    <groupId>pl.joegreen</groupId>
    <artifactId>lambda-from-string</artifactId>
    <version>1.6</version>
</dependency>

We start by defining a translation interface of expressions:

public interface FunctionTranslator<T> {

    void eval(String lambda, LambdaFactory factory) throws LambdaCreationException;

    T getFunction();

}

Translator implementation of an expression that compares integers:

public class CompareIntegersFunctionTranslator implements FunctionTranslator<Function<Integer, Boolean>> {

    private final String TRANSLATOR_PATTERN = "{$var%s} === %s";
    private final TypeReference<Function<Integer, Boolean>> TYPE_REFERENCE = new TypeReference<Function<Integer,Boolean>>(){};

    private String translatedLambda;
    private Function<Integer, Boolean> function;

    @Override
    public void eval(String lambda, LambdaFactory factory) throws LambdaCreationException {
        this.function = factory.createLambda(lambda, TYPE_REFERENCE);       
        this.translatedLambda = translate(lambda);
    }

    @Override
    public Function<Integer, Boolean> getFunction() {
        return function;
    }

    private String translate(String expression) {
        String [] splited = expression.split("");
        return String
            .format(TRANSLATOR_PATTERN, 
                    splited[0].toUpperCase(), 
                    splited[splited.length - 1]);
    }

    @Override
    public String toString() {
        return translatedLambda;
    }   

}

Translator implementation of an expression that sums integers:

public class SumIntegersFunctionTranslator implements FunctionTranslator<Function<Integer, Integer>> {

    private final String TRANSLATOR_PATTERN = "{$var%s} += 1";
    private final TypeReference<Function<Integer, Integer>> TYPE_REFERENCE = new TypeReference<Function<Integer, Integer>>(){};

    private String translatedLambda;
    private Function<Integer, Integer> function;

    @Override
    public void eval(String lambda, LambdaFactory factory) throws LambdaCreationException {
        this.function = factory.createLambda(lambda, TYPE_REFERENCE);       
        this.translatedLambda = translate(lambda);      
    }

    @Override
    public Function<Integer, Integer> getFunction() {
        return function;
    }

    private String translate(String expression) {
        String [] splited = expression.split("");
        return String
            .format(TRANSLATOR_PATTERN, 
                    splited[0].toUpperCase(), 
                    splited[splited.length - 1]);
    }

    @Override
    public String toString() {
        return translatedLambda;
    }   
}

Translation Strategy Implementation:

public enum FunctionTranslatorStrategy {

    COMPARE_INTEGERS(new CompareIntegersFunctionTranslator()),
    SUM_INTEGERS(new SumIntegersFunctionTranslator());

    FunctionTranslator<?> translator;

    FunctionTranslatorStrategy(FunctionTranslator<?> translator) {      
        this.translator = translator;
    }

    public FunctionTranslator<?> getFunctionTranslator() {
        return translator;
    }

}

Implementation of a translator factory:

public class FunctionFactory {

    private static final LambdaFactory LAMBDA_FACTORY = LambdaFactory.get();

    @SuppressWarnings("unchecked")
    public static <T> FunctionTranslator<T> create(
            String lambda, 
            FunctionTranslatorStrategy strategy) throws LambdaCreationException {

        FunctionTranslator<T> translator = (FunctionTranslator<T>) strategy.getFunctionTranslator();
        translator.eval(lambda, LAMBDA_FACTORY);

        return translator;      
    }

}

Testing:

public static void main(String[] args) throws LambdaCreationException {

    FunctionTranslator<Function<Integer, Boolean>> compare = FunctionFactory
            .create("a -> a == 1", 
                    FunctionTranslatorStrategy.COMPARE_INTEGERS);

    System.out.println(compare.toString());             // {$varA} === 1
    System.out.println(compare.getFunction().apply(1)); // true
    System.out.println(compare.getFunction().apply(2)); // false

    FunctionTranslator<Function<Integer, Integer>> sum = FunctionFactory
            .create("b -> b + 1",
                    FunctionTranslatorStrategy.SUM_INTEGERS);       

    System.out.println(sum.toString());             // {$varB} += 1
    System.out.println(sum.getFunction().apply(1)); // 2
    System.out.println(sum.getFunction().apply(2)); // 3
}

Note that there is repeated code and the translation method is very basic (maybe regex to improve). It is possible to greatly improve this implementation and leave the simple things, that's just an idea of where to go.

 3
Author: Tom Melo, 2017-12-29 17:58:15