com.conversantmedia.mapreduce.tool.ExpressionEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for com.conversantmedia.mapreduce.tool.ExpressionEvaluator.java

Source

package com.conversantmedia.mapreduce.tool;

/*
 * #%L
 * Mara Core framework
 * ~~
 * Copyright (C) 2015 Conversant
 * ~~
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ognl.DefaultMemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

import org.apache.commons.lang3.StringUtils;

/**
 *
 *
 */
public class ExpressionEvaluator {

    private static final String TOOL_CONTEXT_KEY = "_context";

    private static ExpressionEvaluator instance;

    private Pattern pattern = null;

    private ExpressionEvaluator() {
        this.pattern = Pattern.compile("(.*)\\$\\{(.+)\\}(.*)", Pattern.DOTALL);
    }

    public static ExpressionEvaluator instance() {
        if (instance == null) {
            instance = new ExpressionEvaluator();
        }
        return instance;
    }

    /**
     * Convenience method for evaluating the provided expression using
     * elements of the annotated tool in the root/context.
     * 
     * @param tool            the tool bean
     * @param expr            the OGNL expression to evaluate
     * @return               the result of the evaluation
     * @throws ToolException   if something goes wrong
     */
    public Object evaluate(AnnotatedTool tool, String expr) throws ToolException {
        return evaluate(tool.getToolBean(), tool.getContext(), expr);
    }

    /**
     * Convenience method for performing expression evaluation on the
     * tool with the context in the context map.
     * 
     * @param tool            the tool bean
     * @param context         the tool context
     * @param expr            the OGNL expression to evaluate
     * @return               the result of the evaluation
     * @throws ToolException   if something goes wrong
     */
    public Object evaluate(Object tool, AnnotatedToolContext context, String expr) throws ToolException {
        Map<String, Object> ognlContext = new HashMap<>();
        ognlContext.put("context", context.getAnnotatedBean());
        return evaluate(tool, ognlContext, expr);
    }

    /**
     *
     * @param rootObj         root object for the OGNL context
     * @param context         set of properties fed into evaluation
     * @param expr            the expression to evaluate. Expects an OGNL expression.
     * @return               the result of the expression's evaluation
     * @throws ToolException   if something goes wrong
     */
    @SuppressWarnings("rawtypes")
    public Object evaluate(Object rootObj, Map<String, Object> context, String expr) throws ToolException {

        Object value = expr;
        Object expressionValue = null;

        Map ognlContext = buildOgnlContext(rootObj, context);

        try {
            Matcher matcher = pattern.matcher(expr);
            while (matcher.matches()) {
                expr = matcher.group(2); // remove the enclosing ${ and }

                // If it doesn't start with 'context.' or 'this.' then prepend the
                // expression with 'this.'
                if (!(StringUtils.startsWith(expr, OgnlContext.THIS_CONTEXT_KEY + ".")
                        || StringUtils.startsWith(expr, "context."))) {
                    expr = "this." + expr;
                } else {
                    expr = StringUtils.replace(expr, "context", TOOL_CONTEXT_KEY);
                }

                expressionValue = Ognl.getValue(expr, ognlContext);
                if (StringUtils.isNotBlank(matcher.group(1)) || StringUtils.isNotBlank(matcher.group(3))) {
                    value = matcher.group(1) + expressionValue + matcher.group(3);
                } else {
                    value = expressionValue;
                }
                matcher = pattern.matcher(value.toString());
            }
        } catch (OgnlException e) {
            throw new ToolException("Failed to evaluate [" + expr + "]", e);
        }
        return value;
    }

    /**
     * Need to do things like replace the 'context' with "_context"
     * since OGNL reserves 'context' for internal use and we want to
     * reference the tool context using it.
     * 
     * @param rootObj   root of the OGNL context
     * @param context   the context map
     * @return         the final Map required by OGNL.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Map buildOgnlContext(Object rootObj, Map<String, Object> context) {
        Map ctx = Ognl.createDefaultContext(rootObj, null, null, new DefaultMemberAccess(true));
        for (Entry<String, Object> e : context.entrySet()) {
            String key = e.getKey().equals("context") ? TOOL_CONTEXT_KEY : e.getKey();
            ctx.put(key, e.getValue());
        }
        return ctx;
    }
}