/*
* File: GridpScriptAssistant.java
* Author: Daniel Rogers
* Created on Mar 19, 2008
*
*/
package gri.gridp.tasks;
import gri.gridp.filematchers.AdvancedFileMatcher;
import gri.gridp.filematchers.FileMatcher;
import gri.gridp.modules.BasicParameter;
import gri.gridp.modules.Flag;
import gri.gridp.modules.Function;
import gri.gridp.modules.Output;
import gri.gridp.modules.Parameter;
import gri.gridp.scripts.ScriptParser;
import gri.gridp.scripts.UnixScriptParser;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Helper class for preparing GRIDP scripts from a given set of
* input parameters. Converts Java objects to the appropriate
* text values for insertion into a script. Also can evaluate
* a script using these values. This object also will store the
* text values that are used in the script and make them available
* for recognition of output files. Oftentimes, output will be based
* upon input and the values of the input will need to be saved
* to properly identify output files.
*
* TODO: this class could be improved.
*
* @author dan.rogers
*/
public class GridpScriptAssistant {
Function function;
//String os = null;
//String shell = null;
File workingDirectory;
ScriptParser scriptParser;
Map txtScriptParams;
// ------------------------------------------------------- Constructors
public GridpScriptAssistant(Function function, File workingDirectory) {
this.function = function;
this.workingDirectory = workingDirectory;
this.scriptParser = new UnixScriptParser();
txtScriptParams = null;
}
// ---------------------------------------------------------- Accessors
public void setFunction(Function function) {
this.function = function;
}
public Function getFunction() {return function;}
public void setWorkingDirectory(File file) {
this.workingDirectory = file;
}
public File getWorkingDirectory() {return workingDirectory;}
public void setScriptParser(ScriptParser parser) {
this.scriptParser = parser;
}
public ScriptParser getScriptParser() {return scriptParser;}
/**
* Returns the script parameters that were used for insertion into
* the script when prepareScript() was called.
*/
public Map getScriptParameters() {
return txtScriptParams;
}
// ----------------------------------------------------- Inputs
public String prepareScript(Map inputs) throws Exception {
//save script params (used by FileMatcher):
Map textParams = new HashMap();
Map scriptParams = new HashMap();
prepareScriptParams(inputs, textParams, scriptParams);
this.txtScriptParams = textParams;
//create script:
String scriptTemplate = function.getScript().getText();
return scriptParser.setVariables(scriptTemplate, scriptParams);
}
/**
* Processes the given input parameters to prepare for script
* execution. The inputs are strings, booleans, and TransferableFiles.
* We will determine the appropriate values to insert into the script
* based on the function definition. Parameters are handled as
* indicated:
*
* Flag:
* A boolean value is expected in the map.
* A default value is used if not present (and defined).
* The text value the Flag has defined for this boolean is then returned
*
* BasicParameter:
* String-type parameters expect Strings in the map.
* Default values are used if not present.
* These are returned without modification.
*
* File-type parameters expected File's.
* These are copied to the working directory.
* The path of the file will be returned for insertion into the script.
*
* @param inputs Map<String,Object> of input objects
* @param textParams Map<String,String> of text values for objects (without scriptlets evaluated)
* @param scriptParams Map<String,String> of script values for objects (with scriptlet evaluated)
*/
protected void prepareScriptParams(Map inputs, Map textParams, Map scriptParams) throws Exception {
List funcParams = function.getParameters();
Parameter param;
for (int i=0; i<funcParams.size(); i++) {
param = (Parameter)funcParams.get(i);
//get param value:
String paramName = param.getName();
Object value = inputs.get(paramName);
//determine text value and script value
String textValue = getTextValue(param, value);
String scriptValue;
if (value == null) {
if (!param.hasNullScriptlet())
throw new MissingParameterException(paramName);
textValue = "";
scriptValue = param.getNullScriptlet();
}
else {
if (param.hasScriptlet()) {
Map scriptletParams = new HashMap(2);
scriptletParams.put(paramName, textValue);
//scriptletParams.put(paramName + ".value", textValue);
scriptValue = scriptParser.setVariables(param.getScriptlet(), scriptletParams);
}
else
scriptValue = textValue;
}
textParams.put(paramName, textValue);
scriptParams.put(paramName, scriptValue);
}
}
/**
* Returns the text value that will be used in the GRIDP script
* for the given parameter. This does not evalulate any scriptlet
* that may exist. If a value is null we will try to obtain the
* default value for the parameter. Otherwise, null will be
* returned.
*
* Additional actions also occur such as storing files before
* returning their names as values in the script.
*/
protected String getTextValue(Parameter param, Object objValue) throws MissingParameterException, IOException {
String textValue;
//flag:
if (param instanceof Flag) {
Flag flagParam = (Flag)param;
//obtain boolean value:
Boolean value = (Boolean)objValue;
if (value == null) {
if (!flagParam.hasDefault())
return null;
value = flagParam.getDefault();
}
//translate boolean value to text we will put in script:
textValue = flagParam.getValue(value.booleanValue());
System.out.println("Flag text: " + textValue);
}
//basic parameter:
else if (param instanceof BasicParameter) {
BasicParameter basicParam = (BasicParameter)param;
if (basicParam.getParameterType().equals("file")) {
File file = (File)objValue;
if (file == null)
return null;
if (!file.getParentFile().equals(this.workingDirectory)) {
File newFile = new File(this.workingDirectory, file.getName());
copy(file, newFile);
textValue = newFile.getName();
}
else
textValue = file.getName();
}
else {
String value = (String)objValue;
if (value == null) {
if (!basicParam.hasDefault())
return null;
value = basicParam.getDefault();
}
//TODO: validate against options
textValue = value;
}
}
//other (should never happen):
else
throw new RuntimeException("Illegal parameter class: " + param.getClass().getName());
return textValue;
}
// ------------------------------------------------------ Outputs
/**
* Searches through the given files and creates a map of output
* files for the outputs generated by the script. A FileMatcher
* is used to do this.
*/
public Map removeOutputs(List files, FileMatcher fileMatcher) {
//search for specified outputs (and remove):
Map outputs = new HashMap();
List outputDefs = function.getOutputs();
Output outputDef;
for (int i=0; i<outputDefs.size(); i++) {
outputDef = (Output)outputDefs.get(i);
fileMatcher.setPattern(outputDef.getPattern());
if (outputDef.getType().equals("file")) {
File matchingFile = removeMatch(files, fileMatcher);
outputs.put(outputDef.getName(), matchingFile);
}
else {
List matchingFiles = removeMatches(files, fileMatcher);
outputs.put(outputDef.getName(), matchingFiles);
}
}
return outputs;
}
/**
* Finds the outputs generated by the script. An AdvancedFileMatcher
* is used to do this and required a map of parameters that it will
* substitute into regular expressions. These are provided as a map
* of name->value pairs.
*/
public Map removeOutputs(List files, Map txtScriptParams) {
return removeOutputs(files, new AdvancedFileMatcher(txtScriptParams));
}
/**
* Finds the outputs generated by the script. The text parameters
* generated for the script in prepareScript() are used for substitution
* into the file patterns. This function can only be called if
* prepareScript() has first been called. Otherwise use findOutputs(File[], Map)
*
* @throws IllegalStateException If you haven't first called prepareScript()
*/
public Map removeOutputs(List files) {
if (this.txtScriptParams == null)
throw new IllegalStateException("This method cannot be invoked if prepareScript() has not been first called. Use findOutputs(File[], Map) instead");
return removeOutputs(files, this.txtScriptParams);
}
/**
* Returns and removes the first file in the list the matcher finds
* (null if none found)
*/
protected File removeMatch(List files, FileMatcher matcher) {
File file;
for (int i=0; i<files.size(); i++) {
file = (File)files.get(i);
if (matcher.matches(file)) {
files.remove(i);
return file;
}
}
return null;
}
/**
* Returns and removes all files in the list that the matcher finds.
* (empty list if none found)
*/
protected List removeMatches(List files, FileMatcher matcher) {
List matches = new ArrayList();
File file;
for (int i=0; i<files.size(); i++) {
file = (File)files.get(i);
if (matcher.matches(file)) {
matches.add(file);
files.remove(i);
i--; //step back since we just shortened list
}
}
return matches;
}
// ---------------------------------------------------- Utility
protected void copy(File from, File to) throws IOException {
FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to);
byte [] bytes = new byte[512];
int bytesRead = 0;
while ((bytesRead = in.read(bytes)) > 0)
out.write(bytes, 0, bytesRead);
out.close();
in.close();
}
// --------------------------------------------- Inner Classes
static class MissingParameterException extends Exception {
String paramName;
public MissingParameterException(String paramName) {
super("Missing parameter: " + paramName + ". No default provided.");
this.paramName = paramName;
}
}
// ------------------------------------------------------- Test
public static void main(String [] args) {
try {
BasicParameter p = new BasicParameter("param");
p.setScriptlet("-p $param");
p.setParameterType("string");
gri.gridp.modules.Script script = new gri.gridp.modules.Script();
script.setText("myprogram $param");
Function f = new Function();
f.addParameter(p);
f.addScript(script);
GridpScriptAssistant assist = new GridpScriptAssistant(f, new File("."));
Map inputs = new HashMap();
inputs.put("param", "value");
String s = assist.prepareScript(inputs);
System.out.println(s);
//System.out.println(assist.getScriptParameters().get("param"));
}
catch(Exception e) {
e.printStackTrace();
}
}
}