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 }