package dovetaildb.scriptbridge;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import dovetaildb.apiservice.ApiException;
import dovetaildb.util.Pair;
import dovetaildb.util.Util;
public class UniversalScriptBridge implements ScriptBridge {
final Map<String, ScriptBridge> bridges = Util.literalMap()
.p("js", new RhinoScriptBridge())
.p("java", new NativeScriptBridge());
public UniversalScriptBridge() {
}
class UniversalScriptEnv extends ScriptEnv {
final HashMap<String, ScriptEnv> envs;
public UniversalScriptEnv(Collection<Pair<String, String>> codeFiles, Map<String, Object> globals) {
envs = new HashMap<String, ScriptEnv>();
HashMap<String, Collection<Pair<String, String>>> bucketedFiles = new HashMap<String, Collection<Pair<String, String>>>();
for(Pair<String,String> codeFile : codeFiles) {
String filename = codeFile.getLeft();
String ext = filename.substring(filename.lastIndexOf('.')+1);
Collection<Pair<String, String>> curFiles = bucketedFiles.get(ext);
if (curFiles == null) {
bucketedFiles.put(ext, curFiles=new ArrayList<Pair<String,String>>());
}
curFiles.add(codeFile);
}
for(Map.Entry<String, Collection<Pair<String, String>>> entry : bucketedFiles.entrySet()) {
String ext = entry.getKey();
ScriptBridge bridge = bridges.get(ext);
if (bridge == null)
throw new ApiException("UnsupportedScriptLanguage","Unsupported file extension: \""+ext+"\"");
ScriptEnv env= bridge.makeEnvFromCodeExecution(entry.getValue(), globals);
envs.put(ext, env);
}
}
@Override
public ScriptFunction getFunction(String functionName, int numParameters) {
int colonPosition = functionName.indexOf(':');
if (colonPosition != -1) {
String scriptType = functionName.substring(0, colonPosition);
functionName = functionName.substring(colonPosition+1);
ScriptEnv env = envs.get(scriptType);
if (env == null)
throw new ApiException("UnsupportedScriptLanguage","Unsupported script language: \""+scriptType+"\" for this function: \""+functionName+"\"");
return env.getFunction(functionName, numParameters);
} else { // not prefixed; try to find in each env:
String lang = null;
ScriptFunction fn = null;
for(Map.Entry<String, ScriptEnv> entry: envs.entrySet()) {
ScriptFunction curFn = entry.getValue().getFunction(functionName, numParameters);
if (curFn != null) {
String curLang = entry.getKey();
if (fn != null) { // multiply defined
throw new ApiException("MultiplyDefinedObject", "This object (\""+functionName+"\" is defined under multiple scripting languages ("+lang+" and "+curLang+")");
}
fn = curFn;
lang = curLang;
}
}
return fn;
}
}
@Override
public Object getObject(String objectName) {
int colonPosition = objectName.indexOf(':');
if (colonPosition == -1)
throw new ApiException("UnspecifiedScriptLanguage", "The object \""+objectName+"\" must be prefixed with the script language; like \"js:"+objectName+"\", for instance.");
String scriptType = objectName.substring(0, colonPosition);
objectName = objectName.substring(colonPosition+1);
ScriptEnv env = envs.get(scriptType);
if (env == null)
throw new ApiException("UnsupportedScriptLanguage","Unsupported script language: \""+scriptType+"\" for this object: \""+objectName+"\"");
return env.getObject(objectName);
}
}
public ScriptEnv makeEnvFromCodeExecution(Collection<Pair<String, String>> codeFiles, Map<String, Object> globals) {
return new UniversalScriptEnv(codeFiles, globals);
}
public Object evaluateExpression(String code, Map<String, Object> globals) {
int colonPosition = code.indexOf(':');
if (colonPosition == -1)
throw new ApiException("ScriptLanguageNotSpecified", "Please prefix your script expression with a language specifier, like \"js:\"");
String scriptType = code.substring(0, colonPosition);
code = code.substring(colonPosition+1);
ScriptBridge bridge = bridges.get(scriptType);
if (bridge == null)
throw new ApiException("UnsupportedScriptLanguage","Unsupported script language: \""+scriptType+"\"");
return bridge.evaluateExpression(code, globals);
}
}