package org.ngbw.pise.commandrenderer; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ngbw.sdk.api.tool.CommandRenderer; import org.ngbw.sdk.common.util.InputStreamCollector; import org.ngbw.sdk.common.util.StringUtils; import org.ngbw.sdk.common.util.SuperString; import org.ngbw.sdk.tool.RenderedCommand; /** * This Class implements CommandRenderer this implementation takes care of * PiseXML and Perl contents *
* @author Rami *
* @author R. Hannes Niedner * */ public class PiseCommandRenderer implements CommandRenderer { private static Log log = LogFactory.getLog(PiseCommandRenderer.class .getName()); //JAXB marshaller with mapped beans in the pise subpackage keyed to the cfg file url private final Map cfgMap = new HashMap(); //pise PiseMarshaller for the submitted cfgFileURL private PiseMarshaller piseMarshaller; //parameter map (parameter name -> parameter value) private Map parameters; // private variable for the toUnixCmd() method private String[] unixCmdGroup = null; private String[] unixCmdGroupNegative = null; private Map inputDataMapTab = null; private RenderedCommand renderedCommand= null; public PiseCommandRenderer() { super(); init(); } private void init() { parameters = null; piseMarshaller = null; unixCmdGroup = new String[100]; unixCmdGroupNegative = new String[100]; inputDataMapTab = new HashMap(); renderedCommand = new RenderedCommand(); } public RenderedCommand render(URL url, Map parameters) { try { if(log.isDebugEnabled()) log.debug("render command from " + parameters.size() + " parameters using config:" + url); init(); this.parameters = parameters; piseMarshaller = initPiseMarshaller(url); List commandList = toUnixCmd(); if(log.isDebugEnabled()) log.debug("toUnixCmd returns a list with " + commandList.size() + " elements"); String[] commandArray = new String[commandList.size()]; renderedCommand.setCommand(commandList.toArray(commandArray)); if (log.isDebugEnabled()) { log.debug("Returned command: " + StringUtils.join(renderedCommand.getCommand(), " ")); Map input = renderedCommand.getInputFileMap(); Map output = renderedCommand.getOutputFileMap(); for(String parameter : input.keySet()) log.debug("Input: " + parameter + ": " + input.get(parameter)); for(String parameter : output.keySet()) log.debug("Output: " + parameter + ": " + output.get(parameter)); } return renderedCommand; } catch (Exception err) { throw new RuntimeException(err); } } //private methods //initialize the JAXB Marshaller private PiseMarshaller initPiseMarshaller(URL url) { if (url == null) throw new NullPointerException("Tool config file URL is null!"); if (cfgMap.containsKey(url) == false) try { PiseMarshaller pm = new PiseMarshaller(url.openStream()); cfgMap.put(url, pm); } catch (IOException e) { throw new RuntimeException("Cannot initialize PiseMarshaller.", e); } return cfgMap.get(url); } //all parameter names in the submitted maps have an underscore appended //thus we declare our own private getters and setters for the value private Set getParameterSet() { Set keySet = new HashSet(); for (String key : parameters.keySet()) { //the regex secefies the only a trailing _ //and the replaceFirst will only take this one String paramName = key.replaceFirst("_$", ""); keySet.add(paramName); } return keySet; } private String getParameterKey(String parameter) { return parameter + "_"; } private String getParameterValue(String parameter) { String key = getParameterKey(parameter); if (parameters.containsKey(key) || parameters.get(key) != null) return new String(parameters.get(key)); else return null; // that is expected since parameters with // null values are notin the parameter map } private void setParameterValue(String parameter, String value) { String key = parameter + "_"; parameters.put(key, value.getBytes()); } private void setInputFileName(String parameter, String fileName) { String key = parameter + "_"; renderedCommand.getInputFileMap().put(key, fileName); } private void setOutputFileName(String parameter, String fileName) { renderedCommand.getOutputFileMap().put(parameter, fileName); } private String evaluatePerlStatement(String perlStatement) throws IOException, InterruptedException, ExecutionException { if(log.isDebugEnabled()) log.debug("PerlStatement: " + perlStatement); // after some test, we had to create a table containing the different element of // the statement, otherwise it seems not working with one String String[] command = new String[3]; command[0] = "perl"; command[1] = "-e"; command[2] = perlStatement; Process worker = Runtime.getRuntime().exec(command); final Future stdOut = InputStreamCollector.readInputStream(worker.getInputStream()); final Future stdErr = InputStreamCollector.readInputStream(worker.getErrorStream()); final int exitCode = worker.waitFor(); if (exitCode != 0) throw new RuntimeException("Exit value was not 0 but " + exitCode + " STDERR: \n" + stdErr.get()); return stdOut.get().trim(); } private List toUnixCmd() throws IOException, InterruptedException, ExecutionException { //String Cmd = null; List commandList = new ArrayList(); //1- A command is constructed from multiple type of parameters: // These are the parameters passed as Map argument for this class, which // means parameter selected through GUI if(log.isDebugEnabled()) log.debug("toUnixCmd: processing " + getParameterSet().size() + " GUI parameters"); for (String paramName : getParameterSet()) processParameter(paramName, false); // These are the parameters hidden from the GUI, but necessary for // generating the command line correctly if(log.isDebugEnabled()) log.debug("toUnixCmd: processing " + piseMarshaller.getHiddenSet().size() + " HIDDEN parameters"); for (String paramName : piseMarshaller.getHiddenSet()) processParameter(paramName, true); // These are the parameters that generate an outfile, they are hidden // from the GUI since the GUI dosen't // give the user the possibility to specify the names of the output // files, we use a standard filenames for each output file // we have added this information (filenames) into the Pise XML file // we return these parameter through getOutputFileMap() if(log.isDebugEnabled()) log.debug("toUnixCmd: processing " + piseMarshaller.getOutfileSet().size() + " OUTFILESET parameters"); for (String paramName : piseMarshaller.getOutfileSet()) processParameter(paramName, false); // These are the parameters passed as Set argument for this class, which // mean that the user has selected a User Data Item // for each of these parameters. we will return these parameters through // getInputFileMap() // if the file is a parameter file, the value of the parameter file is // returned through getInputDataMap() // processInfile_SequenceParameters(); // similar to outfile, one difference is not all output files are // defined as outfile but also as Results log.debug("toUnixCmd: processing " + piseMarshaller.getResultSet().size() + " RESULTSET parameters"); for (String paramName : piseMarshaller.getResultSet()) processParameter(paramName, true); // 2- Arranging inputDataMapTab into // inputDataMap // inputDataMapTab is needed since we have to order // the values with respect // to the Group definition in Pise XML files // we also add the paramfile inside the inputfileMap for(String key : inputDataMapTab.keySet()) { String[] values = inputDataMapTab.get(key); StringBuilder valueSb = new StringBuilder(); for(String value : values) if (value != null) valueSb.append(value); String value = valueSb.toString(); // This replaces the two character sequence of with a single newline character. // Since evalutePerl() returns the stdtout trimmed of leading and trailing whitespace, if we // want to have a parameter be followed by a newline, we need the result of the perl evaluation // to end not with a newline character but with a backslash followed by an n, which we'll replace // with a newline right here. // // note that when you print a java string, as the logging messages in this file do, if // the string contains an actual newline, it will print on multiple lines. If you see // "\n" in the value displayed, it means the string contains two characters: a backslash // followed b an n. This is mostly what we see because if you put "\n" in an xml file // element, the xml unmarshaller delivers this as a java string containing a backslash // followed by an n. // // Both perl (if using double quoted strings) and java interpret "\\n" as the two character // sequence . The first quote escapes the second, yielding a literal // backslash and the n is unquoted. In a perl single quoted string, '\\n' is three characters: // two backslashes followed by an n. If you have a format like '$value\\n', with single quotes, // perl leaves the \\n alone and when we get here we replace the \n with a newline, so we're // left with a single backslash followed by a newline ... probably not what you want. // On the other hand, "$value\\n" works because when ask perl to print this it gives us // the value followed by a backslash follwed by an n. setParameterValue(key, value.replace("\\n", "\n")); setInputFileName(key, key); } // 3- Creating the command line by ordering them with respect to their // group if (unixCmdGroup[0] != null) { commandList.add(unixCmdGroup[0]); if (log.isDebugEnabled()) log.debug("for unixCmdGroup[0] commandList.add: " + unixCmdGroup[0]); } for (int j = 1; j < unixCmdGroup.length; j++) { // Arranging positve groups if (unixCmdGroup[j] != null) { commandList.add(unixCmdGroup[j]); if (log.isDebugEnabled()) log.debug("for unixCmdGroup[" + j + "] commandList.add: " + unixCmdGroup[j]); } } // Arranging the negative groups //for (int k = unixCmdGroupNegative.length - 1; k > 0; k--) { for (int k=1 ; k