blob: bcaa7fb6caf47b571021a50ff4f3b8b9e4bc3d62 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.commandline.data;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.taverna.t2.commandline.exceptions.InputMismatchException;
import net.sf.taverna.t2.commandline.exceptions.InvalidOptionException;
import net.sf.taverna.t2.commandline.exceptions.ReadInputException;
import net.sf.taverna.t2.commandline.options.CommandLineOptions;
import net.sf.taverna.t2.invocation.InvocationContext;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.jdom.JDOMException;
import org.purl.wf4ever.robundle.Bundle;
import uk.org.taverna.databundle.DataBundles;
import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
/**
* Handles the reading, or processing, or input values according to arguments provided to the
* commandline.
* The may be either as direct values, from a file, or from a Baclava document.
* Also handles registering the input values with the Data Service, ready to initiate
* the workflow run.
*
* @author Stuart Owen
*/
public class InputsHandler {
private static Logger logger = Logger.getLogger(InputsHandler.class);
public void checkProvidedInputs(Map<String, InputWorkflowPort> portMap,
CommandLineOptions options) throws InputMismatchException {
// we dont check for the document
Set<String> providedInputNames = new HashSet<String>();
for (int i = 0; i < options.getInputFiles().length; i += 2) {
// If it already contains a value for the input port, e.g
// two inputs are provided for the same port
if (providedInputNames.contains(options.getInputFiles()[i])) {
throw new InputMismatchException(
"Two input values were provided for the same input port "
+ options.getInputFiles()[i] + ".", null, null);
}
providedInputNames.add(options.getInputFiles()[i]);
}
for (int i = 0; i < options.getInputValues().length; i += 2) {
// If it already contains a value for the input port, e.g
// two inputs are provided for the same port
if (providedInputNames.contains(options.getInputValues()[i])) {
throw new InputMismatchException(
"Two input values were provided for the same input port "
+ options.getInputValues()[i] + ".", null, null);
}
providedInputNames.add(options.getInputValues()[i]);
}
if (portMap.size() * 2 != (options.getInputFiles().length + options.getInputValues().length)) {
throw new InputMismatchException(
"The number of inputs provided does not match the number of input ports.",
portMap.keySet(), providedInputNames);
}
for (String portName : portMap.keySet()) {
if (!providedInputNames.contains(portName)) {
throw new InputMismatchException(
"The provided inputs does not contain an input for the port '"
+ portName + "'", portMap.keySet(), providedInputNames);
}
}
}
public Bundle registerInputs(Map<String, InputWorkflowPort> portMap,
CommandLineOptions options, InvocationContext context) throws InvalidOptionException,
ReadInputException, IOException {
Bundle inputDataBundle;
inputDataBundle = DataBundles.createBundle();
inputDataBundle.setDeleteOnClose(false);
System.out.println("Bundle: " + inputDataBundle.getSource());
Path inputs = DataBundles.getInputs(inputDataBundle);
URL url;
try {
url = new URL("file:");
} catch (MalformedURLException e1) {
// Should never happen, but just in case:
throw new ReadInputException(
"The was an internal error setting up the URL to open the inputs. You should contact Taverna support.",
e1);
}
if (options.hasInputFiles()) {
regesterInputsFromFiles(portMap, options, inputs, url);
}
if (options.hasInputValues()) {
registerInputsFromValues(portMap, options, inputs);
}
return inputDataBundle;
}
private void registerInputsFromValues(Map<String, InputWorkflowPort> portMap,
CommandLineOptions options, Path inputs) throws InvalidOptionException {
String[] inputParams = options.getInputValues();
for (int i = 0; i < inputParams.length; i = i + 2) {
String inputName = inputParams[i];
try {
String inputValue = inputParams[i + 1];
InputWorkflowPort port = portMap.get(inputName);
if (port == null) {
throw new InvalidOptionException("Cannot find an input port named '"
+ inputName + "'");
}
Path portPath = DataBundles.getPort(inputs, inputName);
if (options.hasDelimiterFor(inputName)) {
String delimiter = options.inputDelimiter(inputName);
Object value = checkForDepthMismatch(1, port.getDepth(), inputName,
inputValue.split(delimiter));
setValue(portPath, value);
} else {
Object value = checkForDepthMismatch(0, port.getDepth(), inputName, inputValue);
setValue(portPath, value);
}
} catch (IndexOutOfBoundsException e) {
throw new InvalidOptionException("Missing input value for input " + inputName);
} catch (IOException e) {
throw new InvalidOptionException("Error creating value for input " + inputName);
}
}
}
private void regesterInputsFromFiles(Map<String, InputWorkflowPort> portMap,
CommandLineOptions options, Path inputs, URL url) throws InvalidOptionException {
String[] inputParams = options.getInputFiles();
for (int i = 0; i < inputParams.length; i = i + 2) {
String inputName = inputParams[i];
try {
URL inputURL = new URL(url, inputParams[i + 1]);
InputWorkflowPort port = portMap.get(inputName);
if (port == null) {
throw new InvalidOptionException("Cannot find an input port named '"
+ inputName + "'");
}
Path portPath = DataBundles.getPort(inputs, inputName);
if (options.hasDelimiterFor(inputName)) {
String delimiter = options.inputDelimiter(inputName);
Object value = IOUtils.toString(inputURL.openStream()).split(delimiter);
value = checkForDepthMismatch(1, port.getDepth(), inputName, value);
setValue(portPath, value);
} else {
Object value = IOUtils.toByteArray(inputURL.openStream());
value = checkForDepthMismatch(0, port.getDepth(), inputName, value);
setValue(portPath, value);
}
} catch (IndexOutOfBoundsException e) {
throw new InvalidOptionException("Missing input filename for input " + inputName);
} catch (IOException e) {
throw new InvalidOptionException("Could not read input " + inputName + ": "
+ e.getMessage());
}
}
}
private void setValue(Path port, Object userInput) throws IOException {
if (userInput instanceof File) {
DataBundles.setReference(port, ((File) userInput).toURI());
} else if (userInput instanceof URL) {
try {
DataBundles.setReference(port, ((URL) userInput).toURI());
} catch (URISyntaxException e) {
logger.warn(String.format("Error converting %1$s to URI", userInput), e);
}
} else if (userInput instanceof String) {
DataBundles.setStringValue(port, (String) userInput);
} else if (userInput instanceof byte[]) {
Files.write(port, (byte[]) userInput, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
} else if (userInput instanceof List<?>) {
DataBundles.createList(port);
List<?> list = (List<?>) userInput;
for (Object object : list) {
setValue(DataBundles.newListItem(port), object);
}
} else {
logger.warn("Unknown input type : " + userInput.getClass().getName());
}
}
private Object checkForDepthMismatch(int inputDepth, int portDepth, String inputName,
Object inputValue) throws InvalidOptionException {
if (inputDepth != portDepth) {
if (inputDepth < portDepth) {
logger.warn("Wrapping input for '" + inputName + "' from a depth of " + inputDepth
+ " to the required depth of " + portDepth);
while (inputDepth < portDepth) {
List<Object> l = new ArrayList<Object>();
l.add(inputValue);
inputValue = l;
inputDepth++;
}
} else {
String msg = "There is a mismatch between depth of the list for the input port '"
+ inputName + "' and the data presented. The input port requires a "
+ depthToString(portDepth) + " and the data presented is a "
+ depthToString(inputDepth);
throw new InvalidOptionException(msg);
}
}
return inputValue;
}
private String depthToString(int depth) {
switch (depth) {
case 0:
return "single item";
case 1:
return "list";
case 2:
return "list of lists";
default:
return "list of depth " + depth;
}
}
private int getObjectDepth(Object o) {
int result = 0;
if (o instanceof Iterable) {
result++;
Iterator i = ((Iterable) o).iterator();
if (i.hasNext()) {
Object child = i.next();
result = result + getObjectDepth(child);
}
}
return result;
}
}