001    /*******************************************************************************
002     * Portions created by Sebastian Thomschke are copyright (c) 2005-2013 Sebastian
003     * Thomschke.
004     *
005     * All Rights Reserved. This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     *
010     * Contributors:
011     *     Sebastian Thomschke - initial implementation.
012     *******************************************************************************/
013    package net.sf.oval.expression;
014    
015    import java.util.List;
016    import java.util.Map;
017    import java.util.Map.Entry;
018    
019    import javax.script.Bindings;
020    import javax.script.Compilable;
021    import javax.script.CompiledScript;
022    import javax.script.ScriptEngine;
023    import javax.script.ScriptEngineFactory;
024    import javax.script.ScriptEngineManager;
025    import javax.script.ScriptException;
026    
027    import net.sf.oval.Validator;
028    import net.sf.oval.exception.ExpressionEvaluationException;
029    import net.sf.oval.internal.Log;
030    import net.sf.oval.internal.util.ObjectCache;
031    
032    /**
033     * JSR223 Support
034     *
035     * @author Sebastian Thomschke
036     */
037    public class ExpressionLanguageScriptEngineImpl implements ExpressionLanguage
038    {
039            private static final Log LOG = Log.getLog(ExpressionLanguageScriptEngineImpl.class);
040    
041            private static final ScriptEngineManager FACTORY = new ScriptEngineManager();
042    
043            static
044            {
045                    final List<Object> languages = Validator.getCollectionFactory().createList();
046                    for (final ScriptEngineFactory ef : FACTORY.getEngineFactories())
047                            languages.add(ef.getNames());
048                    LOG.info("Available ScriptEngine language names: {1}", languages);
049            }
050    
051            public static ExpressionLanguageScriptEngineImpl get(final String languageId)
052            {
053                    final ScriptEngine engine = FACTORY.getEngineByName(languageId);
054                    return engine == null ? null : new ExpressionLanguageScriptEngineImpl(engine);
055            }
056    
057            private final Compilable compilable;
058            private final ScriptEngine engine;
059            private final ObjectCache<String, CompiledScript> compiledCache;
060    
061            private ExpressionLanguageScriptEngineImpl(final ScriptEngine engine)
062            {
063                    this.engine = engine;
064                    if (engine instanceof Compilable)
065                    {
066                            compilable = (Compilable) engine;
067                            compiledCache = new ObjectCache<String, CompiledScript>();
068                    }
069                    else
070                    {
071                            compilable = null;
072                            compiledCache = null;
073                    }
074            }
075    
076            /**
077             * {@inheritDoc}
078             */
079            public Object evaluate(final String expression, final Map<String, ? > values) throws ExpressionEvaluationException
080            {
081                    LOG.debug("Evaluating JavaScript expression: {1}", expression);
082                    try
083                    {
084                            final Bindings scope = engine.createBindings();
085                            for (final Entry<String, ? > entry : values.entrySet())
086                                    scope.put(entry.getKey(), entry.getValue());
087    
088                            if (compilable != null)
089                            {
090                                    CompiledScript compiled = compiledCache.get(expression);
091                                    if (compiled == null)
092                                    {
093                                            compiled = compilable.compile(expression);
094                                            compiledCache.put(expression, compiled);
095                                    }
096                                    return compiled.eval(scope);
097                            }
098                            return engine.eval(expression, scope);
099                    }
100                    catch (final ScriptException ex)
101                    {
102                            throw new ExpressionEvaluationException("Evaluating JavaScript expression failed: " + expression, ex);
103                    }
104            }
105    
106            /**
107             * {@inheritDoc}
108             */
109            public boolean evaluateAsBoolean(final String expression, final Map<String, ? > values) throws ExpressionEvaluationException
110            {
111                    final Object result = evaluate(expression, values);
112                    if (!(result instanceof Boolean)) throw new ExpressionEvaluationException("The script must return a boolean value.");
113                    return (Boolean) result;
114            }
115    }