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 }