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}