JexlExpressionEvaluator.java

package com.github.mygreen.messageformatter.expression;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.springframework.expression.EvaluationException;
import org.springframework.util.Assert;

import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

/**
 * JEXLによる式を評価する {@link ExpressionEvaluator} の実装。
 *
 *
 * @author T.TSUCHIE
 *
 */
@Slf4j
public class JexlExpressionEvaluator implements ExpressionEvaluator {

    /**
     * パースしたEL式のキャッシュ
     */
    protected final ObjectCache<String, Expression> expressionCache = new ObjectCache<>();

    /**
     * JEXLの処理エンジン。
     */
    @Getter
    private final JexlEngine jexlEngine;

    /**
     * EL式の処理エンジンを指定してインスタンスを作成します。
     * @param jexlEngine JEXLの処理エンジン。
     */
    public JexlExpressionEvaluator(@NonNull JexlEngine jexlEngine) {
        this.jexlEngine = jexlEngine;
    }

    /**
     * 標準設定の処理エンジンを元にインスタンスを作成します。
     * <p>プレフィックス{@code f} でカスタム関数 {@link CustomFunctions} が登録されています。</p>
     */
    public JexlExpressionEvaluator() {
        JexlEngine engine = new JexlEngine();

        // EL式中で使用可能な関数の登録
        Map<String, Object> functions = new HashMap<>();
        functions.put("f", CustomFunctions.class);
        engine.setFunctions(functions);

        this.jexlEngine = engine;
    }

    @Override
    public Object evaluate(@NonNull String expression, @NonNull Map<String, Object> variables) {

        Assert.hasLength(expression, "expression should not be empty.");

        if(log.isDebugEnabled()) {
            log.debug("Evaluating JEXL expression: {}", expression);
        }

        try {
            Expression expr = expressionCache.get(expression);
            if (expr == null) {
                expr = jexlEngine.createExpression(expression);
                expressionCache.put(expression, expr);
            }

            return expr.evaluate(new MapContext(variables));

        } catch(Exception ex) {
            throw new EvaluationException(String.format("Evaluating [%s] script with JEXL failed.", expression), ex);
        }
    }
}