org.jspringbot.keyword.expression.ExpressionHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jspringbot.keyword.expression.ExpressionHelper.java

Source

/*
 * Copyright (c) 2012. JSpringBot. All Rights Reserved.
 *
 * See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The JSpringBot licenses this file to You 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.
 */

package org.jspringbot.keyword.expression;

import de.odysseus.el.TreeValueExpression;
import de.odysseus.el.tree.Node;
import org.jspringbot.keyword.expression.engine.DefaultELContext;
import org.jspringbot.keyword.expression.engine.function.SupportedFunctionsManager;
import org.jspringbot.keyword.expression.plugin.ExpressionHandler;
import org.jspringbot.keyword.expression.plugin.ExpressionHandlerManager;
import org.jspringbot.keyword.expression.plugin.VariableProviderManager;
import org.jspringbot.syntax.HighlightRobotLogger;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ReflectionUtils;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpressionHelper implements ApplicationContextAware, ValueEvaluator {

    public static final HighlightRobotLogger LOG = HighlightRobotLogger.getLogger(ExpressionHelper.class);

    private static final Pattern EXPRESSION_PATTERN = Pattern.compile("\\$\\[(.*)\\]", Pattern.CASE_INSENSITIVE);

    private static final Pattern PREFIX_EXPRESSION_PATTERN = Pattern.compile("([a-z0-9]+)\\:(.*)",
            Pattern.CASE_INSENSITIVE);

    private static final Field nodeField;

    static {
        nodeField = ReflectionUtils.findField(TreeValueExpression.class, "node");
        nodeField.setAccessible(true);
    }

    private ExpressionFactory factory;

    private SupportedFunctionsManager functionManager;

    private ExpressionHandlerManager expressionManager;

    private VariableProviderManager variableManager;

    private ELExpressionHandler defaultHandler = new ELExpressionHandler();

    public ExpressionHelper(ExpressionFactory factory) {
        this.factory = factory;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        functionManager = new SupportedFunctionsManager(context);
        expressionManager = new ExpressionHandlerManager(this, context, defaultHandler);
        variableManager = new VariableProviderManager(context);
    }

    public void evaluationShouldBe(String expression, Object expected) throws Exception {
        Object value = silentEvaluate(expression);

        if (expected == null && value == null) {
            return;
        }

        Object actualValue = silentEvaluate(expected);

        LOG.keywordAppender().appendArgument("Expected Value", value);
        LOG.keywordAppender().appendArgument("Actual Value", actualValue);

        if (expected == null || value == null || !actualValue.equals(value)) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not expected.", actualValue));
        }
    }

    public void evaluationShouldBeNull(String expression) throws Exception {
        Object value = evaluate(expression);

        if (value != null) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' is not null.", expression));
        }
    }

    public void evaluationShouldNotBeNull(String expression) throws Exception {
        Object value = evaluate(expression);

        if (value == null) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' is null.", expression));
        }
    }

    public void evaluationShouldBeTrue(String expression) throws Exception {
        Object value = evaluate(expression);

        if (value == null) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not true.", expression));
        }

        if (!Boolean.class.isInstance(value)) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not true.", expression));
        }

        if (!Boolean.TRUE.equals(value)) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not true.", expression));
        }
    }

    public void evaluationShouldBeFalse(String expression) throws Exception {
        Object value = evaluate(expression);

        if (value == null) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not false.", expression));
        }

        if (!Boolean.class.isInstance(value)) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not false.", expression));
        }

        if (!Boolean.FALSE.equals(value)) {
            throw new IllegalArgumentException(String.format("Evaluation '%s' was not false.", expression));
        }
    }

    public Object silentEvaluate(Object param) throws Exception {
        if (String.class.isInstance(param) && isSupported((String) param)) {
            try {
                LOG.keywordAppender().createPath();

                return evaluate((String) param);
            } finally {
                LOG.keywordAppender().endPath();
            }
        }

        return param;
    }

    private void initVariables(List<Object> variables) throws Exception {
        Map<String, Object> scopedVariables = ScopedVariableHolder.peek();
        int i = 0;
        while (scopedVariables.containsKey(String.format("$%d", i + 1))) {
            i++;
        }
        for (Object variable : variables) {
            ScopedVariableHolder.peek().put(String.format("$%d", i + 1), silentEvaluate(variable));
            i++;
        }
    }

    public Object variableScope(List<Object> variables, Callable<Object> callable) throws Exception {
        try {
            ScopedVariableHolder.push(new HashMap<String, Object>());
            initVariables(variables);

            return callable.call();
        } finally {
            ScopedVariableHolder.pop();
        }
    }

    public void variableScope(List<Object> variables, Runnable runnable) throws Exception {
        try {
            ScopedVariableHolder.push(new HashMap<String, Object>());
            initVariables(variables);

            runnable.run();
        } finally {
            ScopedVariableHolder.pop();
        }
    }

    public Object evaluate(String expression) throws Exception {
        Matcher matcher = EXPRESSION_PATTERN.matcher(expression);

        if (!matcher.find()) {
            throw new IllegalArgumentException(String.format("Invalid expression format '%s'.", expression));
        }

        String content = matcher.group(1);

        LOG.keywordAppender().appendExpression(expression);

        Matcher prefixMatcher = PREFIX_EXPRESSION_PATTERN.matcher(content);
        if (prefixMatcher.matches()) {
            String prefix = prefixMatcher.group(1);
            String prefixContent = prefixMatcher.group(2);

            return expressionManager.evaluation(prefix, prefixContent);
        } else {
            return expressionManager.defaultEvaluation(content);
        }
    }

    public boolean isSupported(String expression) {
        Matcher matcher = EXPRESSION_PATTERN.matcher(expression);

        return matcher.matches();
    }

    private Map<String, Object> getVariables() {
        Map<String, Object> variables = new HashMap<String, Object>();

        variables.putAll(ScopedVariableHolder.peek());
        variables.putAll(variableManager.getVariables());

        return variables;
    }

    public Object getValue(Object result) {
        DefaultELContext context = new DefaultELContext(functionManager, getVariables());
        ValueExpression expr = factory.createValueExpression(result, TypeExpressionHolder.peek());

        return expr.getValue(context);
    }

    public class ELExpressionHandler implements ExpressionHandler {

        @Override
        public String getPrefix() {
            throw new UnsupportedOperationException(
                    "This is the default handler. This method should not be called.");
        }

        @Override
        public Object evaluate(String expression) throws Exception {
            LOG.keywordAppender().appendProperty("Expression Handler", "Expression Language (JUEL)");

            DefaultELContext context = new DefaultELContext(functionManager, getVariables());
            TreeValueExpression expr = (TreeValueExpression) factory.createValueExpression(context,
                    String.format("${%s}", expression), TypeExpressionHolder.peek());

            Set<String> vars = VariableNodeCapture.capture((Node) nodeField.get(expr));
            for (Map.Entry<String, Object> var : getVariables().entrySet()) {
                if (vars.contains(var.getKey())) {
                    LOG.keywordAppender().appendProperty(String.format("EL Variable ['%s']", var.getKey()),
                            var.getValue());
                }
            }

            Object result = expr.getValue(context);

            LOG.keywordAppender().appendProperty("EL Result", result);

            return result;
        }
    }
}