    001package com.hammurapi.common;
    002
    003import java.io.InputStream;
    004import java.io.InputStreamReader;
    005import java.util.Locale;
    006import java.util.Map;
    007
    008import javax.script.ScriptEngine;
    009import javax.script.ScriptEngineFactory;
    010import javax.script.ScriptEngineManager;
    011import javax.script.ScriptException;
    012import javax.script.SimpleBindings;
    013
    014/**
    015 * This class resolves and evaluates scripts for a class.
    016 * Scripts and properties for script parameterization are resolved traversing class hierarchy.
    017 * A special resource with .binder extension is used to create custom bindings for
    018 * script evaluation. This resource shall contain a fragment of java code. This code shall reference a
    019 * single parameter 'bindings' to access existing bindings and create new ones.
    020 *
    021 * The primary purpose of this class is to be a facility for flexible rendering/code generation.
    022 * @author Pavel Vlasov
    023 *
    024 */
    025public class ScriptProcessor {
    026
    027        private ResourceLoader rl;
    028        private ScriptEngineManager scriptEngineManager;
    029
    030        public ScriptProcessor(ResourceLoader resourceLoader, ClassLoader scriptEnginesClassLoader) {
    031                this.rl = resourceLoader;
    032                this.scriptEngineManager = scriptEnginesClassLoader==null ? new ScriptEngineManager() : new ScriptEngineManager(scriptEnginesClassLoader);
    033        }
    034
    035        public Object eval(String scriptName, Map<String, Object> bindings, Locale locale) throws ScriptException {
    036                for (ScriptEngineFactory sef: scriptEngineManager.getEngineFactories()) {
    037                        for (String extension: sef.getExtensions()) {
    038                                InputStream scriptStream = rl.getResourceStream(scriptName, locale, extension);
    039                                if (scriptStream!=null) {
    040                                        SimpleBindings sBindings = new SimpleBindings();
    041                                        Map<String, Object> props = rl.getProperties(scriptName, locale, "properties");
    042                                        sBindings.putAll(props);
    043                                        if (bindings!=null) {
    044                                                sBindings.putAll(bindings);
    045                                        }
    046                                        InputStream binderStream = rl.getResourceStream(scriptName, locale, "binder");
    047                                        if (binderStream!=null) {
    048                                                ScriptEngine be = scriptEngineManager.getEngineByExtension("java");
    049                                                if (be==null) {
    050                                                        throw new ScriptException("Cannot evaluate binder - java script engine not found");
    051                                                }
    052                                                SimpleBindings bb = new SimpleBindings();
    053                                                bb.put("bindings", sBindings);
    054                                                be.eval(new InputStreamReader(binderStream), bb);
    055                                        }
    056                                        return sef.getScriptEngine().eval(new InputStreamReader(scriptStream), sBindings);
    057                                }
    058                        }
    059                }
    060                throw new ScriptException("Script resource not found: "+scriptName+" in "+rl);
    061        }
    062
    063}