blob: d124b7a0dbe10059071bbf1fa728b4068c2092bf [file] [log] [blame]
/**
* <copyright>
* Copyright (c) 2010-2014 Henshin developers. All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* </copyright>
*/
package org.eclipse.emf.henshin.model.util;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* Script engine wrapper for automatic handling of Java imports.
*/
public class ScriptEngineWrapper {
private static final Pattern WILDCARD_PATTERN = Pattern.compile("(.*)\\.\\*$");
private static final Pattern DIR_FQN_PATTERN = Pattern.compile("(.*)\\.([a-z][^.]*)*$");
private static final Pattern CLASS_FQN_PATTERN = Pattern.compile("(.*)\\.([A-Z][^.]*)$");
/**
* The original scripting engine to delegate to.
*/
private final ScriptEngine engine;
/**
* List of global imports.
*/
private final List<String> globalImports;
/**
* Constructor.
*
* @param engine Script engine to be used.
* @param globalImports List of global Java imports.
*/
public ScriptEngineWrapper(ScriptEngine engine, String[] globalImports) {
this.engine = engine;
this.globalImports = new ArrayList<String>();
if (globalImports != null) {
for (int i = 0; i < globalImports.length; i++) {
this.globalImports.add(globalImports[i]);
}
}
if (engine == null) {
System.err.println("Warning: cannot find JavaScript engine");
}
}
/**
* Constructor.
*
* @param globalImports List of global Java imports.
*/
public ScriptEngineWrapper(String[] globalImports) {
this(new ScriptEngineManager().getEngineByName("JavaScript"), globalImports);
}
/**
* Get the wrapped JavaScript engine.
*
* @return The wrapped JavaScript engine.
*/
public ScriptEngine getEngine() {
return engine;
}
/**
* Evaluates a given expression in a context which is extended with the provided imports. The imports are on purpose
* not added to the global scope to prevent pollution of the namespace.
*
* @param script Script to be executed.
* @param localImports List of imports.
* @return The result.
* @throws ScriptException On script execution errors.
*/
@SuppressWarnings("unchecked")
public Object eval(String script, List<String> localImports) throws ScriptException {
if (!globalImports.isEmpty() || !localImports.isEmpty()) {
script = toStringWithImports(script,globalImports, localImports);
}
return engine.eval(script);
}
/**
* Converts a list of imports like List("foo.Foo", "foo.bar.*") into one string "foo.Foo, foo.bar"
*/
private static String toStringWithImports(String script, List<String>... imports) {
StringBuffer out = new StringBuffer();
String delim = "";
out.append("with (new JavaImporter(");
for (int i = 0; i < imports.length; i++) {
for (String entry : imports[i]) {
if (isDirectory(entry) || isWildcard(entry)) {
out.append(delim).append(stripWildcard(entry));
delim = ", ";
}
}
}
out.append(")) { ");
for (int i = 0; i < imports.length; i++) {
for (String entry : imports[i]) {
Matcher m = CLASS_FQN_PATTERN.matcher(entry);
if (m.matches()) {
String filename = m.group(2);
out.append("var ");
out.append(filename);
out.append(" = Java.type('");
out.append(entry);
out.append("'); ");
}
}
}
out.append(script);
out.append( " }");
return out.toString();
}
private static String stripWildcard(String imp) {
return isWildcard(imp) ? imp.substring(0, imp.length() - 2) : imp;
}
private static boolean isDirectory(String imp) {
return DIR_FQN_PATTERN.matcher(imp).matches();
}
private static boolean isWildcard(String imp) {
return WILDCARD_PATTERN.matcher(imp).matches();
}
}