/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator.nio.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import com.rapidminer.operator.IOObject;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.operator.ports.metadata.SimplePrecondition;
import com.rapidminer.parameter.ParameterHandler;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeFile;
import com.rapidminer.parameter.PortProvider;
import com.rapidminer.parameter.conditions.PortConnectedCondition;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.WebServiceTools;
/**
* Provides methods for creating and working with a file InputPort. Used by reading operators.
*
* @author Dominik Halfkann
*
*/
public class FileInputPortHandler {
private InputPort fileInputPort;
private String fileParameterName;
private Operator operator;
/**
*
* @param fileParameterName
* has to be a path to a file or a URL. If it is a URL the file will be downloaded
* and temporary saved.
*/
public FileInputPortHandler(Operator operator, InputPort fileInputPort, String fileParameterName) {
this.fileInputPort = fileInputPort;
this.fileParameterName = fileParameterName;
this.operator = operator;
SimplePrecondition precondition = new SimplePrecondition(fileInputPort, new MetaData(FileObject.class)) {
@Override
protected boolean isMandatory() {
return false;
}
};
fileInputPort.addPrecondition(precondition);
}
private URL fileCachedForURL;
private File cachedFile;
/**
* Returns either the selected file referenced by the value of the parameter with the name
* {@link #getFileParameterName()} or the file delivered at {@link #fileInputPort}. Which of
* these options is chosen is determined by the parameter {@link #PARAMETER_DESTINATION_TYPE}.
* */
public File getSelectedFile() throws OperatorException {
if (!(fileInputPort.isConnected() || fileInputPort.getPorts().getOwner().getOperator().getProcess() == null
&& fileInputPort.getAnyDataOrNull() != null)) {
String fileParameter = operator.getParameterAsString(fileParameterName);
try {
URL url = new URL(fileParameter);
// Check file:// manually to avoid copying to temp file
if ("file".equals(url.getProtocol())) {
return new File(url.getFile());
} else if (fileCachedForURL != null && fileCachedForURL.equals(url)) {
// for other URL protocols, download and return temp file,
// but use cache in case
// method is called twice.
return cachedFile;
} else {
try {
cachedFile = File.createTempFile("rm_file_", ".dump");
cachedFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(cachedFile);
Tools.copyStreamSynchronously(WebServiceTools.openStreamFromURL(url), fos, true);
} catch (IOException e) {
throw new OperatorException("Failed to access URL: " + url, e);
}
fileCachedForURL = url;
return cachedFile;
}
} catch (MalformedURLException e) {
File file = operator.getParameterAsFile(fileParameterName);
return file;
}
} else {
return fileInputPort.getData(FileObject.class).getFile();
}
}
/**
* Same as {@link #getSelectedFile()}, but opens the stream.
* */
public InputStream openSelectedFile() throws OperatorException, IOException {
if (!(fileInputPort.isConnected() || fileInputPort.getPorts().getOwner().getOperator().getProcess() == null
&& fileInputPort.getAnyDataOrNull() != null)) {
return new FileInputStream(getSelectedFile());
} else {
return fileInputPort.getData(FileObject.class).openStream();
}
}
/**
* Same as {@link #getSelectedFile()}, but returns true if file is specified (in the respective
* way).
* */
public boolean isFileSpecified() {
if (!(fileInputPort.isConnected() || fileInputPort.getPorts().getOwner().getOperator().getProcess() == null
&& fileInputPort.getAnyDataOrNull() != null)) {
return operator.isParameterSet(fileParameterName);
} else {
try {
return fileInputPort.getData(IOObject.class) != null;
} catch (OperatorException e) {
return false;
}
}
}
/**
* Creates the file parameter named by fileParameterName that depends on whether or not the port
* returned by the given PortProvider is connected.
*
* @param parameterHandler
* used to check dependencies
* @param parameterName
* Name of the parameter that is created
* @param description
* Description of the parameter
* @param portProvider
* port which accepts the FileObject. If this port is connected, the parameter will
* be hidden.
* @param fileExtensions
* allowed file types.
* */
public static ParameterType makeFileParameterType(ParameterHandler parameterHandler, String parameterName,
String description, PortProvider portProvider, String... fileExtensions) {
return makeFileParameterType(parameterHandler, parameterName, description, portProvider, false, fileExtensions);
}
/**
* Creates the file parameter named by fileParameterName that depends on whether or not the port
* returned by the given PortProvider is connected.
*
* @param parameterHandler
* used to check dependencies
* @param parameterName
* Name of the parameter that is created
* @param description
* Description of the parameter
* @param portProvider
* port which accepts the FileObject. If this port is connected, the parameter will
* be hidden.
* @param addAllFileFormatsFilter
* defines whether a filter for all file extension should be added as default filter
* for the file chooser dialog. This makes most sense for file reading operations
* that allow to read files with multiple file extensions. For file writing
* operations it is not recommended as the new filter will not add the correct file
* ending when entering the path of a file that does not exist.
* @param fileExtensions
* allowed file types.
* */
public static ParameterType makeFileParameterType(ParameterHandler parameterHandler, String parameterName,
String description, PortProvider portProvider, boolean addAllFileExtensionsFilter, String... fileExtensions) {
String[] fileExtArray = new String[fileExtensions.length];
int i = 0;
for (String fileExtension : fileExtensions) {
fileExtArray[i++] = fileExtension;
}
final ParameterTypeFile fileParam = new ParameterTypeFile(parameterName, description, true, fileExtArray);
fileParam.setExpert(false);
fileParam.setAddAllFileExtensionsFilter(addAllFileExtensionsFilter);
fileParam.registerDependencyCondition(new PortConnectedCondition(parameterHandler, portProvider, true, false));
return fileParam;
}
/**
* Uses a default description and a single file extension.
*
* @see #makeFileParameterType(ParameterHandler, String, String, PortProvider, String...)
* */
public static ParameterType makeFileParameterType(ParameterHandler parameterHandler, String parameterName,
String fileExtension, PortProvider portProvider) {
return makeFileParameterType(parameterHandler, parameterName, "Name of the file to read the data from.",
fileExtension, portProvider);
}
/**
* Uses a single allowed file extension.
*
* @see #makeFileParameterType(ParameterHandler, String, String, PortProvider, String...)
* */
public static ParameterType makeFileParameterType(ParameterHandler parameterHandler, String parameterName,
String description, String fileExtension, PortProvider portProvider) {
return makeFileParameterType(parameterHandler, parameterName, description, portProvider, fileExtension);
}
/**
* Adds a new (file-)InputPortNotConnectedCondition for a given parameter.
*
* @param parameter
* @param parameterHandler
* @param portProvider
*/
public static void addFileDependencyCondition(ParameterType parameter, ParameterHandler parameterHandler,
PortProvider portProvider) {
parameter.registerDependencyCondition(new PortConnectedCondition(parameterHandler, portProvider, true, false));
}
/**
* Returns the specified filename or "InputFileObject" if the file OutputPort is connected.
*
* @return
* @throws OperatorException
*/
public String getSelectedFileDescription() throws OperatorException {
if (!fileInputPort.isConnected()) {
return operator.getParameterAsString(fileParameterName);
} else {
return "InputFileObject";
}
}
}