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}