org.apache.commons.scxml.env.jexl.JexlEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.commons.scxml.env.jexl.JexlEvaluator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.commons.scxml.env.jexl;

import java.io.Serializable;
import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.Script;
import org.apache.commons.scxml.Context;
import org.apache.commons.scxml.Evaluator;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.w3c.dom.Node;

/**
 * Evaluator implementation enabling use of JEXL expressions in SCXML documents.
 * 
 */
public class JexlEvaluator implements Evaluator, Serializable {

    /** Serial version UID. */
    private static final long serialVersionUID = 1L;

    /** Error message if evaluation context is not a JexlContext. */
    private static final String ERR_CTX_TYPE = "Error evaluating JEXL "
            + "expression, Context must be a org.apache.commons.jexl.JexlContext";

    /** Pattern for recognizing the SCXML In() special predicate. */
    private static Pattern inFct = Pattern.compile("In\\(");
    /** Pattern for recognizing the Commons SCXML Data() builtin function. */
    private static Pattern dataFct = Pattern.compile("Data\\(");

    JexlEngine jexlEngine = new JexlEngine();

    /** Constructor. */
    public JexlEvaluator() {
        super();
        jexlEngine.setSilent(false);
        jexlEngine.setStrict(false);
    }

    /**
     * Evaluate an expression.
     * 
     * @param ctx
     *            variable context
     * @param expr
     *            expression
     * @return a result of the evaluation
     * @throws SCXMLExpressionException
     *             For a malformed expression
     * @see Evaluator#eval(Context, String)
     */
    @Override
    public Object eval(final Context ctx, final String expr) throws SCXMLExpressionException {
        if (expr == null)
            return null;
        JexlContext jexlCtx = null;
        if (ctx instanceof JexlContext) {
            jexlCtx = (JexlContext) ctx;
        } else
            throw new SCXMLExpressionException(ERR_CTX_TYPE);
        Expression exp = null;
        try {
            String evalExpr = inFct.matcher(expr).replaceAll("_builtin.isMember(_ALL_STATES, ");
            evalExpr = dataFct.matcher(evalExpr).replaceAll("_builtin.data(_ALL_NAMESPACES, ");
            exp = jexlEngine.createExpression(evalExpr);
            return exp.evaluate(getEffectiveContext(jexlCtx));
        } catch (Exception e) {
            throw new SCXMLExpressionException("eval('" + expr + "'):" + e.getMessage(), e);
        }
    }

    /**
     * @see Evaluator#evalCond(Context, String)
     */
    @Override
    public Boolean evalCond(final Context ctx, final String expr) throws SCXMLExpressionException {
        if (expr == null)
            return null;
        JexlContext jexlCtx = null;
        if (ctx instanceof JexlContext) {
            jexlCtx = (JexlContext) ctx;
        } else
            throw new SCXMLExpressionException(ERR_CTX_TYPE);
        Expression exp = null;
        try {
            String evalExpr = inFct.matcher(expr).replaceAll("_builtin.isMember(_ALL_STATES, ");
            evalExpr = dataFct.matcher(evalExpr).replaceAll("_builtin.data(_ALL_NAMESPACES, ");
            exp = jexlEngine.createExpression(evalExpr);
            return (Boolean) exp.evaluate(getEffectiveContext(jexlCtx));
        } catch (Exception e) {
            e.printStackTrace();
            throw new SCXMLExpressionException("evalCond('" + expr + "'):" + e.getMessage(), e);
        }
    }

    /**
     * @see Evaluator#evalLocation(Context, String)
     */
    @Override
    public Node evalLocation(final Context ctx, final String expr) throws SCXMLExpressionException {
        if (expr == null)
            return null;
        JexlContext jexlCtx = null;
        if (ctx instanceof JexlContext) {
            jexlCtx = (JexlContext) ctx;
        } else
            throw new SCXMLExpressionException(ERR_CTX_TYPE);
        Expression exp = null;
        try {
            String evalExpr = inFct.matcher(expr).replaceAll("_builtin.isMember(_ALL_STATES, ");
            evalExpr = dataFct.matcher(evalExpr).replaceFirst("_builtin.dataNode(_ALL_NAMESPACES, ");
            evalExpr = dataFct.matcher(evalExpr).replaceAll("_builtin.data(_ALL_NAMESPACES, ");
            exp = jexlEngine.createExpression(evalExpr);
            return (Node) exp.evaluate(getEffectiveContext(jexlCtx));
        } catch (Exception e) {
            throw new SCXMLExpressionException("evalLocation('" + expr + "'):" + e.getMessage(), e);
        }
    }

    /**
     * @see Evaluator#evalScript(Context, String)
     */
    @Override
    public Object evalScript(Context ctx, String script) throws SCXMLExpressionException {
        if (script == null)
            return null;
        JexlContext jexlCtx = null;
        if (ctx instanceof JexlContext) {
            jexlCtx = (JexlContext) ctx;
        } else
            throw new SCXMLExpressionException(ERR_CTX_TYPE);
        Script jexlScript = null;
        try {
            String scriptStr = inFct.matcher(script).replaceAll("_builtin.isMember(_ALL_STATES, ");
            scriptStr = dataFct.matcher(scriptStr).replaceAll("_builtin.data(_ALL_NAMESPACES, ");
            jexlScript = jexlEngine.createScript(scriptStr);
            return jexlScript.execute(getEffectiveContext(jexlCtx));
        } catch (Exception e) {
            throw new SCXMLExpressionException("evalScript('" + script + "'):" + e.getMessage(), e);
        }
    }

    /**
     * Create a new child context.
     * 
     * @param parent
     *            parent context
     * @return new child context
     * @see Evaluator#newContext(Context)
     */
    @Override
    public Context newContext(final Context parent) {
        return new JexlContext(parent);
    }

    /**
     * Create a new context which is the summation of contexts from the current
     * state to document root, child has priority over parent in scoping rules.
     * 
     * @param nodeCtx
     *            The JexlContext for this state.
     * @return The effective JexlContext for the path leading up to document
     *         root.
     */
    protected JexlContext getEffectiveContext(final JexlContext nodeCtx) {
        return new JexlContext(new EffectiveContextMap(nodeCtx));
    }

    /**
     * The map that will back the effective context for the
     * {@link JexlEvaluator}. The effective context enables the chaining of
     * {@link Context}s all the way from the current state node to the root.
     * 
     */
    private static final class EffectiveContextMap extends AbstractMap<String, Object> {

        /** The {@link Context} for the current state. */
        final Context leaf;

        /** Constructor. */
        public EffectiveContextMap(JexlContext ctx) {
            super();
            this.leaf = ctx;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Set<Map.Entry<String, Object>> entrySet() {
            Set<Map.Entry<String, Object>> entrySet = new HashSet<Map.Entry<String, Object>>();
            Context current = leaf;
            while (current != null) {
                entrySet.addAll(current.getVars().entrySet());
                current = current.getParent();
            }
            return entrySet;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Object put(String key, Object value) {
            Object old = leaf.get(key);
            if (leaf.has(key)) {
                leaf.set(key, value);
            } else {
                leaf.setLocal(key, value);
            }
            return old;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Object get(Object key) {
            Context current = leaf;
            while (current != null) {
                if (current.getVars().containsKey(key))
                    return current.getVars().get(key);
                current = current.getParent();
            }
            return null;
        }

    }

}