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.CompilationCustomizer; 015import org.codehaus.groovy.control.customizers.ImportCustomizer; 016 017import groovy.lang.Closure; 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(Cache cache, CompilationCustomizer... customizers) { 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 // apply AST transform (e.g. make script cancellable) 041 for (CompilationCustomizer customizer : customizers) { 042 configuration.addCompilationCustomizers(customizer); 043 } 044 045 // cache .class files for compiled script classes 046 return GroovyEngine.newCachedCompiledScriptEngine(configuration, cache); 047 } 048 049 public static final String ARGV_BINDING_NAME = "args"; 050 public static final String SHELL_BINDING_NAME = "__shell"; 051 public static final String SHELL_CLI_BINDING_NAME = "__cli"; 052 public static final String SHELL_ARGS_BINDING_NAME = "__args"; 053 public static final String SHELL_DEFS_BINDING_NAME = "__defs"; 054 055 private final GroovyEngine engine; 056 private final ScriptProvider resolver; 057 058 public ScriptShell() { 059 this(null, null, emptyMap()); 060 } 061 062 public ScriptShell(ScriptProvider resolver, CmdlineInterface cli, Map<String, ?> globals) { 063 this(resolver, cli, globals, Cache.getCache("script_classes", CacheType.Persistent)); 064 } 065 066 public ScriptShell(ScriptProvider resolver, CmdlineInterface cli, Map<String, ?> globals, Cache cache, CompilationCustomizer... customizers) { 067 this.engine = createScriptEngine(cache, customizers); 068 this.resolver = resolver; 069 070 // setup bindings 071 Bindings bindings = engine.createBindings(); 072 bindings.putAll(globals); 073 074 // bind API objects 075 bindings.put(SHELL_BINDING_NAME, this); 076 bindings.put(SHELL_CLI_BINDING_NAME, cli); 077 078 // setup script context 079 engine.getContext().setBindings(bindings, ScriptContext.GLOBAL_SCOPE); 080 } 081 082 public Object runScript(String name, Bindings bindings) throws Throwable { 083 return evaluate(resolver.getScript(name), bindings); 084 } 085 086 public Closure<?> callable(String script) throws Throwable { 087 // evaluate user script 088 Object value = evaluate(script, engine.createBindings()); 089 // value as target type 090 return GroovyEngine.asType(value, Closure.class); 091 } 092 093 public Object evaluate(String script, Bindings bindings) throws Throwable { 094 try { 095 return engine.eval(script, bindings); 096 } catch (Throwable t) { 097 throw sanitize(t); 098 } 099 } 100 101 public Throwable sanitize(Throwable t) { 102 // unwrap nested exception 103 while (t.getClass() == ScriptException.class && t.getCause() != null) { 104 t = t.getCause(); 105 } 106 // remove Groovy runtime methods from Java stack trace 107 return GroovyEngine.sanitizeStackTrace(t); 108 } 109 110 /** 111 * Scripting API methods for compiling String option values to executable objects 112 */ 113 114 public GroovyComparator comparator(String script) throws Throwable { 115 return GroovyComparator.wrap(callable(script)); 116 } 117 118 public GroovyAction action(String script) throws Throwable { 119 return GroovyAction.wrap(callable(script)); 120 } 121 122 public ExpressionFormat format(String expression) throws Throwable { 123 return new ExpressionFormat(expression); 124 } 125 126 public ExpressionFilter filter(String expression) throws Throwable { 127 return new ExpressionFilter(expression); 128 } 129 130 public ExpressionMapper mapper(String expression) throws Throwable { 131 return new ExpressionMapper(expression); 132 } 133 134 public QueryExpression query(String expression) throws Throwable { 135 return new QueryExpression(expression); 136 } 137 138}