001    /*******************************************************************************
002     * Portions created by Sebastian Thomschke are copyright (c) 2005-2012 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.Map;
016    import java.util.Map.Entry;
017    
018    import net.sf.oval.exception.ExpressionEvaluationException;
019    import net.sf.oval.internal.Log;
020    import net.sf.oval.internal.util.ObjectCache;
021    
022    import org.mozilla.javascript.Context;
023    import org.mozilla.javascript.ContextFactory;
024    import org.mozilla.javascript.EvaluatorException;
025    import org.mozilla.javascript.Script;
026    import org.mozilla.javascript.Scriptable;
027    
028    /**
029     * @author Sebastian Thomschke
030     *
031     */
032    public class ExpressionLanguageJavaScriptImpl implements ExpressionLanguage
033    {
034            private static final Log LOG = Log.getLog(ExpressionLanguageJavaScriptImpl.class);
035    
036            private final Scriptable parentScope;
037    
038            private final ObjectCache<String, Script> scriptCache = new ObjectCache<String, Script>();
039    
040            /**
041             * Default constructor.
042             */
043            public ExpressionLanguageJavaScriptImpl()
044            {
045                    final Context ctx = ContextFactory.getGlobal().enterContext();
046                    try
047                    {
048                            parentScope = ctx.initStandardObjects();
049                    }
050                    finally
051                    {
052                            Context.exit();
053                    }
054            }
055    
056            /**
057             * {@inheritDoc}
058             */
059            public Object evaluate(final String expression, final Map<String, ? > values) throws ExpressionEvaluationException
060            {
061                    LOG.debug("Evaluating JavaScript expression: {1}", expression);
062                    try
063                    {
064                            final Context ctx = ContextFactory.getGlobal().enterContext();
065                            Script script = scriptCache.get(expression);
066                            if (script == null)
067                            {
068                                    ctx.setOptimizationLevel(9);
069                                    script = ctx.compileString(expression, "<cmd>", 1, null);
070                                    scriptCache.put(expression, script);
071                            }
072                            final Scriptable scope = ctx.newObject(parentScope);
073                            scope.setPrototype(parentScope);
074                            scope.setParentScope(null);
075                            for (final Entry<String, ? > entry : values.entrySet())
076                                    scope.put(entry.getKey(), scope, Context.javaToJS(entry.getValue(), scope));
077                            return script.exec(ctx, scope);
078                    }
079                    catch (final EvaluatorException ex)
080                    {
081                            throw new ExpressionEvaluationException("Evaluating JavaScript expression failed: " + expression, ex);
082                    }
083                    finally
084                    {
085                            Context.exit();
086                    }
087            }
088    
089            /**
090             * {@inheritDoc}
091             */
092            public boolean evaluateAsBoolean(final String expression, final Map<String, ? > values)
093                            throws ExpressionEvaluationException
094            {
095                    final Object result = evaluate(expression, values);
096                    if (!(result instanceof Boolean))
097                            throw new ExpressionEvaluationException("The script must return a boolean value.");
098                    return (Boolean) result;
099            }
100    }