001package net.filebot.cli;
002
003import static java.util.Collections.*;
004import static net.filebot.util.RegularExpressions.*;
005
006import java.util.Map;
007import java.util.ResourceBundle;
008
009import javax.script.Bindings;
010import javax.script.ScriptContext;
011import javax.script.ScriptException;
012
013import org.codehaus.groovy.control.CompilerConfiguration;
014import org.codehaus.groovy.control.customizers.ImportCustomizer;
015
016import groovy.lang.Closure;
017
018import net.filebot.Cache;
019import net.filebot.CacheType;
020import net.filebot.GroovyEngine;
021import net.filebot.format.ExpressionFilter;
022import net.filebot.format.ExpressionFormat;
023import net.filebot.format.ExpressionMapper;
024import net.filebot.format.QueryExpression;
025
026public class ScriptShell {
027
028        private static GroovyEngine createScriptEngine() {
029                ResourceBundle bundle = ResourceBundle.getBundle(ScriptShell.class.getName());
030
031                CompilerConfiguration configuration = new CompilerConfiguration();
032                configuration.setScriptBaseClass(bundle.getString("scriptBaseClass"));
033
034                // default imports
035                ImportCustomizer imports = new ImportCustomizer();
036                imports.addStarImports(COMMA.split(bundle.getString("starImport")));
037                imports.addStaticStars(COMMA.split(bundle.getString("starStaticImport")));
038                configuration.addCompilationCustomizers(imports);
039
040                // cache .class files for compiled script classes
041                return GroovyEngine.newCachedCompiledScriptEngine(configuration, Cache.getCache("script_classes", CacheType.Persistent));
042        }
043
044        public static final String ARGV_BINDING_NAME = "args";
045        public static final String SHELL_BINDING_NAME = "__shell";
046        public static final String SHELL_CLI_BINDING_NAME = "__cli";
047        public static final String SHELL_ARGS_BINDING_NAME = "__args";
048        public static final String SHELL_DEFS_BINDING_NAME = "__defs";
049
050        private final GroovyEngine engine;
051        private final ScriptProvider resolver;
052
053        public ScriptShell() {
054                this(null, null, emptyMap());
055        }
056
057        public ScriptShell(ScriptProvider resolver, CmdlineInterface cli, Map<String, ?> globals) {
058                this.engine = createScriptEngine();
059                this.resolver = resolver;
060
061                // setup bindings
062                Bindings bindings = engine.createBindings();
063                bindings.putAll(globals);
064
065                // bind API objects
066                bindings.put(SHELL_BINDING_NAME, this);
067                bindings.put(SHELL_CLI_BINDING_NAME, cli);
068
069                // setup script context
070                engine.getContext().setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
071        }
072
073        public Object runScript(String name, Bindings bindings) throws Throwable {
074                return evaluate(resolver.getScript(name), bindings);
075        }
076
077        public Closure<?> callable(String script) throws Throwable {
078                // evaluate user script
079                Object value = evaluate(script, engine.createBindings());
080                // value as target type
081                return GroovyEngine.asType(value, Closure.class);
082        }
083
084        public Object evaluate(String script, Bindings bindings) throws Throwable {
085                try {
086                        return engine.eval(script, bindings);
087                } catch (Throwable t) {
088                        throw sanitize(t);
089                }
090        }
091
092        public Throwable sanitize(Throwable t) {
093                // unwrap nested exception
094                while (t.getClass() == ScriptException.class && t.getCause() != null) {
095                        t = t.getCause();
096                }
097                // remove Groovy runtime methods from Java stack trace
098                return GroovyEngine.sanitizeStackTrace(t);
099        }
100
101        /**
102         * Scripting API methods for compiling String option values to executable objects
103         */
104
105        public GroovyComparator comparator(String script) throws Throwable {
106                return GroovyComparator.wrap(callable(script));
107        }
108
109        public GroovyAction action(String script) throws Throwable {
110                return GroovyAction.wrap(callable(script));
111        }
112
113        public ExpressionFormat format(String expression) throws Throwable {
114                return new ExpressionFormat(expression);
115        }
116
117        public ExpressionFilter filter(String expression) throws Throwable {
118                return new ExpressionFilter(expression);
119        }
120
121        public ExpressionMapper mapper(String expression) throws Throwable {
122                return new ExpressionMapper(expression);
123        }
124
125        public QueryExpression query(String expression) throws Throwable {
126                return new QueryExpression(expression);
127        }
128
129}