001package com.randomnoun.common.jessop; 002 003/* (c) 2016 randomnoun. All Rights Reserved. This work is licensed under a 004 * BSD Simplified License. ( http://www.randomnoun.com/bsd-simplified.html ) 005 */ 006 007import java.io.ByteArrayOutputStream; 008import java.io.IOException; 009import java.io.PrintWriter; 010import java.io.Reader; 011import java.io.StringReader; 012 013import javax.script.AbstractScriptEngine; 014import javax.script.Bindings; 015import javax.script.Compilable; 016import javax.script.CompiledScript; 017import javax.script.ScriptContext; 018import javax.script.ScriptEngine; 019import javax.script.ScriptEngineFactory; 020import javax.script.ScriptEngineManager; 021import javax.script.ScriptException; 022import javax.script.SimpleBindings; 023 024import org.apache.log4j.Logger; 025 026import com.randomnoun.common.jessop.lang.JavascriptJessopScriptBuilder; 027 028/** The jessop ScriptEngine class. 029 * 030 * @author knoxg 031 */ 032public class JessopScriptEngine extends AbstractScriptEngine implements Compilable { 033 034 /** Logger instance for this class */ 035 Logger logger = Logger.getLogger(JessopScriptEngine.class); 036 037 /** ScriptEngineFactory that created this class */ 038 ScriptEngineFactory factory; 039 040 /** Reserved key for a named value that identifies the initial language used for jessop scripts. 041 * If not set, will default to 'javascript' 042 */ 043 public static final String JESSOP_LANGUAGE = "com.randommoun.common.jessop.language"; 044 045 /** Default value for the JESSOP_LANGUAGE key; has the value "javascript" */ 046 public static final String JESSOP_DEFAULT_LANGUAGE = "javascript"; 047 048 /** Reserved key for a named value that identifies the initial ScriptEngine used for jessop scripts. 049 * If not set, will use the default engine for the default language. 050 */ 051 public static final String JESSOP_ENGINE = "com.randommoun.common.jessop.engine"; 052 053 /** Default value for the JESSOP_ENGINE key; has the value "rhino" */ 054 public static final String JESSOP_DEFAULT_ENGINE = "rhino"; 055 056 057 /** Reserved key for a named value that sets the initial exception converter. 058 * If not set, will use the default converter for the default language 059 */ 060 public static final String JESSOP_EXCEPTION_CONVERTER = "com.randommoun.common.jessop.exceptionConverter"; 061 062 /** Default value for the JESSOP_EXCEPTION_CONVERTER key; has the value null */ 063 public static final String JESSOP_DEFAULT_EXCEPTION_CONVERTER = null; 064 065 /** Reserved key for a named value that sets the initial bindings converter. 066 * If not set, will use the default converter for the default language 067 */ 068 public static final String JESSOP_BINDINGS_CONVERTER = "com.randommoun.common.jessop.bindingsConverter"; 069 070 /** Default value for the JESSOP_EXCEPTION_CONVERTER key; has the value null */ 071 // so this isn't final any more, since it might change depending on what's on the classpath 072 public static String JESSOP_DEFAULT_BINDINGS_CONVERTER; // = "com.randomnoun.common.jessop.engine.jvmRhino.JvmRhinoBindingsConverter"; 073 074 static { 075 JavascriptJessopScriptBuilder jjsb = new JavascriptJessopScriptBuilder(); 076 JESSOP_DEFAULT_BINDINGS_CONVERTER = jjsb.getDefaultBindingsConverterClassName(); 077 } 078 079 080 /** Reserved key for a named value that controls whether the target script is compiled 081 * (providing the target engine allows it). 082 * If not set, will default to 'false'. 083 */ 084 public static final String JESSOP_COMPILE_TARGET = "com.randommoun.common.jessop.compileTarget"; 085 086 /** Default value for the JESSOP_COMPILE_TARGET key; has the value "false" */ 087 public static final String JESSOP_DEFAULT_COMPILE_TARGET = "false"; 088 089 090 /** Reserved key for a named value that controls whether the target script 091 * will have EOLs suppressed after scriptlets that appear at the end of a line. 092 * If not set, will default to 'false'. 093 */ 094 public static final String JESSOP_SUPPRESS_EOL = "com.randommoun.common.jessop.suppressEol"; 095 096 /** Default value for the JESSOP_SUPPRESS_EOL key; has the value "false" */ 097 public static final String JESSOP_DEFAULT_SUPPRESS_EOL = "false"; 098 099 100 // so I guess we implement this twice then 101 // let's always compile it if we can 102 103 /** {@inheritDoc} */ 104 @Override 105 public Object eval(String script, ScriptContext context) throws ScriptException { 106 CompiledScript cscript = compile(script); 107 return cscript.eval(context); 108 } 109 110 /** {@inheritDoc} */ 111 @Override 112 public Object eval(Reader reader, ScriptContext context) throws ScriptException { 113 CompiledScript cscript = compile(reader); 114 return cscript.eval(context); 115 } 116 117 // if the user doesn't supply a context, we evaluate it with a null context (not the default context) 118 // the JessopCompiledScript will then use the appropriate language's default context instead 119 120 /** {@inheritDoc} */ 121 @Override 122 public Object eval(String script) throws ScriptException { 123 return eval(script, (ScriptContext) null); 124 } 125 126 /** {@inheritDoc} */ 127 @Override 128 public Object eval(Reader reader) throws ScriptException { 129 return eval(reader, (ScriptContext) null); 130 } 131 132 /** {@inheritDoc} */ 133 @Override 134 public Bindings createBindings() { 135 return new SimpleBindings(); 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public ScriptEngineFactory getFactory() { 141 if (factory != null) { 142 return factory; 143 } else { 144 return new JessopScriptEngineFactory(); 145 } 146 } 147 148 // package private; called by ScriptEngineFactory only 149 void setEngineFactory(ScriptEngineFactory fac) { 150 factory = fac; 151 } 152 153 @Override 154 /** {@inheritDoc} */ 155 public CompiledScript compile(String script) throws ScriptException { 156 return compile(new StringReader(script)); 157 } 158 159 /** {@inheritDoc} */ 160 @Override 161 public CompiledScript compile(Reader script) throws ScriptException { 162 try { 163 // if (initialLanguage == null) { initialLanguage = "javascript"; } 164 165 JessopDeclarations declarations = new JessopDeclarations(); 166 // jessop defaults 167 declarations.setEngine(JESSOP_DEFAULT_ENGINE); 168 declarations.setExceptionConverter(JESSOP_DEFAULT_EXCEPTION_CONVERTER); 169 declarations.setBindingsConverter(JESSOP_DEFAULT_BINDINGS_CONVERTER); 170 declarations.setCompileTarget(Boolean.valueOf(JESSOP_DEFAULT_COMPILE_TARGET)); 171 declarations.setSuppressEol(Boolean.valueOf(JESSOP_DEFAULT_SUPPRESS_EOL)); 172 173 // ScriptEngine defaults 174 String filename = (String) get(ScriptEngine.FILENAME); 175 String initialLanguage = (String) get(JessopScriptEngine.JESSOP_LANGUAGE); 176 String initialEngine = (String) get(JessopScriptEngine.JESSOP_ENGINE); 177 String initialExceptionConverter = (String) get(JessopScriptEngine.JESSOP_EXCEPTION_CONVERTER); 178 String initialBindingsConverter = (String) get(JessopScriptEngine.JESSOP_BINDINGS_CONVERTER); 179 String initialCompileTarget = (String) get(JessopScriptEngine.JESSOP_COMPILE_TARGET); 180 String initialSuppressEol = (String) get(JessopScriptEngine.JESSOP_SUPPRESS_EOL); 181 if (initialLanguage==null) { initialLanguage = JESSOP_DEFAULT_LANGUAGE; } 182 if (filename!=null) { declarations.setFilename(filename); } 183 if (initialEngine!=null) { declarations.setEngine(initialEngine); } 184 if (initialExceptionConverter!=null) { declarations.setExceptionConverter(initialExceptionConverter); } 185 if (initialBindingsConverter!=null) { declarations.setBindingsConverter(initialBindingsConverter); } 186 if (initialCompileTarget!=null) { declarations.setCompileTarget(Boolean.valueOf(initialCompileTarget)); } 187 if (initialSuppressEol!=null) { declarations.setSuppressEol(Boolean.valueOf(initialSuppressEol)); } 188 189 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 190 PrintWriter pw = new PrintWriter(baos); 191 // new JavascriptJessopScriptBuilder(); // default for now 192 JessopScriptBuilder jsb = ((JessopScriptEngineFactory) getFactory()).getJessopScriptBuilderForLanguage(initialLanguage); 193 jsb.setPrintWriter(pw); 194 Tokeniser t = new Tokeniser(this, jsb); 195 jsb.setTokeniserAndDeclarations(t, declarations); 196 197 // tokenise the script 198 int ch = script.read(); 199 while (ch!=-1) { 200 t.parseChar((char) ch); 201 ch = script.read(); 202 } 203 t.parseEndOfFile(); 204 pw.flush(); 205 206 // get the output from the PrintWriter 207 String newScript = baos.toString(); 208 209 // the final JSB contains the final declarations that were in effect 210 declarations = t.jsb.getDeclarations(); 211 212 // get this from the jessop declaration eventally, but for now: 213 // if the underlying engine supports compilation, then compile that here, otherwise just store the source 214 ScriptEngine engine = new ScriptEngineManager().getEngineByName(declarations.engine); // nashorn in JDK9 215 if (engine==null) { 216 throw new ScriptException("java.scriptx engine '" + declarations.engine + "' not found"); 217 } 218 219 JessopBindingsConverter jbc = null; 220 if (declarations.bindingsConverter !=null && !declarations.bindingsConverter.equals("")) { 221 try { 222 jbc = (JessopBindingsConverter) 223 Class.forName(declarations.bindingsConverter).newInstance(); 224 } catch (Exception e) { 225 throw (ScriptException) new ScriptException( 226 "bindingsConverter '" + declarations.bindingsConverter + "' not loaded").initCause(e); 227 } 228 } 229 230 JessopExceptionConverter jec = null; 231 if (declarations.exceptionConverter!=null && !declarations.exceptionConverter.equals("")) { 232 try { 233 jec = (JessopExceptionConverter) 234 Class.forName(declarations.exceptionConverter).newInstance(); 235 } catch (Exception e) { 236 throw (ScriptException) new ScriptException( 237 "exceptionConverter '" + declarations.exceptionConverter + "' not loaded").initCause(e); 238 } 239 } 240 241 // com.sun.script.javascript.RhinoScriptEngine m = (com.sun.script.javascript.RhinoScriptEngine) engine; 242 // the newScript is compiled here, if the engine supports it 243 return new JessopCompiledScript(engine, declarations.isCompileTarget(), 244 declarations.getFilename(), newScript, jec, jbc); 245 246 } catch (IOException ioe) { 247 throw new ScriptException(ioe); 248 } 249 250 251 252 } 253}