/*******************************************************************************
* Copyright (c) 2008-2010, G. Weirich and Elexis
* 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
*
* Contributors:
* G. Weirich - initial implementation
*
*******************************************************************************/
package ch.elexis.data;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import ch.elexis.core.constants.TextContainerConstants;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.data.constants.ExtensionPointConstantsData;
import ch.elexis.core.data.events.ElexisEventDispatcher;
import ch.elexis.core.data.interfaces.scripting.Interpreter;
import ch.elexis.core.data.util.Extensions;
import ch.elexis.core.exceptions.ElexisException;
import ch.rgw.io.FileTool;
import ch.rgw.tools.ExHandler;
import ch.rgw.tools.StringTool;
/**
* A script. At this moment only beanshell is supported as interpreter, but others are possible
*
* @author gerry
*
*/
public class Script extends NamedBlob2 {
public static final String INTERPRETER_BEANSHELL = "BSH";
public static final String INTERPRETER_SCALA = "SCALA";
public static final String INTERPRETER_DEFAULT = INTERPRETER_BEANSHELL;
private static final Pattern varPattern = Pattern
.compile(TextContainerConstants.MATCH_TEMPLATE);
private static final String PREFIX = "Script:";
public static final String SCRIPT_MARKER = "SCRIPT:";
private Interpreter interpreter = null;
/**
*
* @param name
* @return
* @throws ElexisException
* if the script interpreter can not successfully be loaded or is not available.
*/
private static Interpreter getInterpreter(String name) throws ElexisException{
if (name == null)
name = INTERPRETER_BEANSHELL;
List<IConfigurationElement> scripters =
Extensions.getExtensions(ExtensionPointConstantsData.SCRIPTING);
for (IConfigurationElement scripter : scripters) {
if (scripter.getAttribute("name").equals(name)) {
try {
return (Interpreter) scripter.createExecutableExtension("class");
} catch (CoreException e) {
ExHandler.handle(e);
throw new ElexisException(Script.class, "Could not load intepreter "
+ e.getMessage(), ElexisException.EE_NOT_SUPPORTED);
}
}
}
throw new ElexisException(Script.class, name + " interpreter plug-in not available",
ElexisException.EE_NOT_SUPPORTED);
}
/**
* create and return an appropriate Interpreter for the given script. If the script declares an
* Interpreter as in / * SCALA * /, the metghod will attempr to load this interpreter. If no
* such declaration is found, the default interpreter will be returned
*
* @param script
* the script contents.
* @return the Interpreter
* @throws ElexisException
* if the interpreter was not found or could not be instantiated
*/
private void loadInterpreter(String script) throws ElexisException{
if (interpreter == null) {
if (script == null) {
script = getString();
}
Pattern ip = Pattern.compile("^\\/\\*\\s*!([A-Z]+)!\\s*\\*\\/", Pattern.MULTILINE);
Matcher m = ip.matcher(script);
if (m.matches()) {
interpreter = getInterpreter(m.group(1));
} else {
interpreter = getInterpreter(null);
}
}
}
public static Script create(String name, String contents) throws ElexisException{
String mid = PREFIX + name;
Script ret = new Script(mid);
if (ret.state() == INEXISTENT) {
ret.create(mid);
} else if (ret.state() == DELETED) {
ret.undelete();
}
if (StringTool.isNothing(contents)) {
contents = "/* !BSH! */";
}
ret.putString(contents);
return ret;
}
@Override
public String getLabel(){
String[] name = getId().split(":");
return name[1];
}
public void init() throws ElexisException{
loadInterpreter(null);
interpreter.setValue("finished", false);
interpreter.setValue("init", true);
interpreter.run(parse(getString(), new PersistentObject[0]), false);
interpreter.setValue("init", false);
}
public void finished() throws ElexisException{
loadInterpreter(null);
interpreter.setValue("finished", true);
interpreter.run(parse(getString(), (PersistentObject[]) null), false);
}
public void setVariable(String name, Object value) throws ElexisException{
loadInterpreter(null);
interpreter.setValue(name, value);
}
/**
* Replace variables of the form [Patient.Name] in the script with their respective values for
* the current call
*
* @param script
* the script
* @param params
* all Variables to replace
* @return the parsed Script
*/
private static String parse(String script, PersistentObject... params){
if (params == null) {
params = new PersistentObject[0];
}
Matcher matcher = varPattern.matcher(script);
// Suche Variablen der Form [Patient.Alter]
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
boolean bMatched = false;
String var = matcher.group().replaceAll("[\\[\\]]", StringTool.leer);
String[] fields = var.split("\\.");
if (fields.length > 1) {
String fqname = "ch.elexis.data." + fields[0];
for (PersistentObject o : params) {
if (o.getClass().getName().equals(fqname)) {
String repl = o.get(fields[1]);
repl = repl.replace('\\', '/');
repl = repl.replace('\"', ' ');
repl = repl.replace('\n', ' ');
repl = repl.replace('\r', ' ');
matcher.appendReplacement(sb, "\"" + repl + "\"");
bMatched = true;
}
}
}
if (!bMatched) {
matcher.appendReplacement(sb, "\"\"");
}
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* execute a script entered as string with the given interpreter
*
*
* @param objects
* optional Objects to replace in Variables like [Fall.Grund] in the script
* @param params
* optional parameters. These can be of the form <i>name=value</i> or <i>value</i>.
* if no name is given, the variables will be inserted for $1, $2 ... in the script.
* If a name is given, $names in the script will be replaced with the respective
* values.
* @return The result of the script interpreter
* @throws ElexisException
*/
public static Object execute(Interpreter scripter, String script, String params,
PersistentObject... objects) throws ElexisException{
if (!StringTool.isNothing(script)) {
if (params != null) {
String var = "\\$";
String[] parameters = params.split("\\s*,\\s*");
for (int i = 0; i < parameters.length; i++) {
String parm = parameters[i].trim();
String[] p = parm.split("=");
if (p.length == 2) {
script = script.replaceAll("\\" + p[0], p[1]);
} else {
script = script.replaceAll(var + i, p[0]);
}
}
}
String parsed = parse(script, objects);
scripter.setValue("actPatient", ElexisEventDispatcher.getSelectedPatient());
scripter.setValue("actFall", ElexisEventDispatcher.getSelected(Fall.class));
scripter.setValue("actKons", ElexisEventDispatcher.getSelected(Konsultation.class));
scripter.setValue("actMandant", CoreHub.actMandant);
scripter.setValue("actUser", CoreHub.actUser);
scripter.setValue("Elexis", CoreHub.plugin);
return scripter.run(parsed, true);
}
return null;
}
public Object execute(String params, PersistentObject... objects) throws ElexisException{
String script = getString();
loadInterpreter(script);
return execute(interpreter, script, params, objects);
}
public static List<Script> getScripts(){
Query<Script> qbe = new Query<Script>(Script.class);
qbe.add("ID", "LIKE", PREFIX + "%");
return qbe.execute();
}
/**
* Execute a script that is part of the call
*
* @param call
* e.g. scriptname(a="foo",b="bar")
* @param objects
* some Objects to cinvert in the script
* @return the result of the interpreter
* @throws ElexisException
* if no such scruiopt was found or an error occurred
*/
public static Object executeScript(String call, PersistentObject... objects)
throws ElexisException{
call = call.trim();
String name = call;
String params = null;
int x = name.indexOf('(');
if (x != -1) {
name = call.substring(0, x);
params = call.substring(x + 1, call.length() - 1);
}
Query<Script> qbe = new Query<Script>(Script.class);
qbe.add("ID", Query.EQUALS, PREFIX + name);
List<Script> found = qbe.execute();
if (found.size() == 0) {
throw new ElexisException(Script.class,
"A Script with this name was not found " + name, ElexisException.EE_NOT_FOUND);
}
Script script = found.get(0);
try {
return script.execute(params, objects);
} catch (Exception e) {
ExHandler.handle(e);
throw new ElexisException(Script.class, "Error while executing " + name + ": "
+ e.getMessage(), ElexisException.EE_UNEXPECTED_RESPONSE);
}
}
@Override
public boolean isDragOK(){
return true;
}
@Override
public boolean isValid(){
if (getId().matches(PREFIX + "[a-zA-Z0-9_-]+")) {
return super.isValid();
}
return false;
}
public static Script load(String id){
Script ret = new Script(id);
if (ret.isValid()) {
return ret;
}
return null;
}
protected Script(String id){
super(id);
}
protected Script(){}
public static Interpreter getInterpreterFor(String script) throws ElexisException{
Script s = new Script();
s.loadInterpreter(script);
return s.interpreter;
}
public static Script importFromFile(String filepath) throws ElexisException{
File file = new File(filepath);
if (!file.exists()) {
file = new File(filepath + ".script");
if (!file.exists()) {
throw new ElexisException(Script.class, "Could not find file " + filepath,
ElexisException.EE_NOT_FOUND);
}
}
try {
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
StringBuilder sb = new StringBuilder();
String in;
while ((in = br.readLine()) != null) {
sb.append(in);
}
br.close();
String name = FileTool.getNakedFilename(filepath);
return create(name, sb.toString());
} catch (IOException ioex) {
throw new ElexisException(Script.class, "Error loading file " + filepath + ": "
+ ioex.getMessage(), ElexisException.EE_FILE_ERROR);
}
}
}