10000 Add input provider plugins by imagejan · Pull Request #18 · scijava/batch-processor · GitHub
[go: up one dir, main page]

Skip to content

Add input provider plugins #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add input provider plugins
This change makes batch processing extensible by allowing plugins implementing the BatchInputProvider interface to support custom script parameter inputs, such as net.imagej.Dataset which will be provided in the imagej-plugins-batch project.

Because BatchService now extends HandlerService, I needed to rename the supports() methods more concisely into supportsModule() and supportsItem(), to avoid overriding supports() from HandlerService.
Since we are still pre-1.0.0, we can change the API freely, but bump the version to 0.2.0-SNAPSHOT to reflect this API change.
  • Loading branch information
imagejan committed Aug 10, 2018
commit c503a13ae668916c13985dbb3bbae69b83df6f7c
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>

<artifactId>batch-processor</artifactId>
<version>0.1.4-SNAPSHOT</version>
<version>0.2.0-SNAPSHOT</version>

<name>Batch Processor</name>
<description>A Batch Processor for SciJava Modules and Scripts</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public class BatchModuleSearchActionFactory implements SearchActionFactory {
@Override
public boolean supports(final SearchResult result) {
return (result instanceof ModuleSearchResult)
&& batchService.supports(((ModuleSearchResult) result).info());
&& batchService.supportsModule(((ModuleSearchResult) result).info());
}

@Override
public SearchAction create(final SearchResult result) {
return new DefaultSearchAction("Batch", true, () -> {
return new DefaultSearchAction("Batch", () -> {
batchService.run(((ModuleSearchResult) result).info());
});
}
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/org/scijava/batch/BatchService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,34 @@
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.scijava.batch.input.BatchInput;
import org.scijava.batch.input.BatchInputProvider;
import org.scijava.module.Module;
import org.scijava.module.ModuleInfo;
import org.scijava.module.ModuleItem;
import org.scijava.plugin.HandlerService;
import org.scijava.service.SciJavaService;

public interface BatchService extends SciJavaService {
public interface BatchService extends HandlerService<BatchInput, BatchInputProvider<?>>, SciJavaService {
/**
* Returns true if {@code moduleInfo} has at least one input item whose type
* is supported by this service
*/
default public boolean supports(ModuleInfo moduleInfo) {
default public boolean supportsModule(ModuleInfo moduleInfo) {
for (ModuleItem<?> input : moduleInfo.inputs()) {
if (supports(input.getType()))
if (supportsItem(input))
return true;
}
return false;
}

//default public getHandler(ModuleItem)

/**
* Returns true if {@code type} can be populated with batch inputs provided
* by this service
*/
public boolean supports(Class<?> type);
public boolean supportsItem(ModuleItem<?> moduleItem);

/**
* Run the module described by {@link ModuleInfo} in batch.
Expand All @@ -38,7 +44,14 @@ default public boolean supports(ModuleInfo moduleInfo) {
*/
default public List<ModuleItem<?>> batchableInputs(ModuleInfo moduleInfo) {
return StreamSupport.stream(moduleInfo.inputs().spliterator(), false)
.filter(item -> supports(item.getType()))
.filter(item -> supportsItem(item))
.collect(Collectors.toList());
}

/**
* Fill a provided ModuleItem with a given input object
* @param <I>
*/
public <I> void fillInput(Module module, ModuleItem<?> moduleItem, I inputObject);

}
50 changes: 44 additions & 6 deletions src/main/java/org/scijava/batch/FileBatchService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,48 @@
import java.util.HashMap;

import org.scijava.Priority;
import org.scijava.batch.input.BatchInput;
import org.scijava.batch.input.BatchInputProvider;
import org.scijava.command.CommandService;
import org.scijava.log.LogService;
import org.scijava.module.Module;
import org.scijava.module.ModuleInfo;
import org.scijava.module.ModuleItem;
import org.scijava.plugin.AbstractHandlerService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.service.AbstractService;
import org.scijava.service.Service;

@Plugin(type = Service.class, priority = Priority.LOW)
public final class FileBatchService extends AbstractService implements
BatchService {
public final class FileBatchService extends AbstractHandlerService<BatchInput, BatchInputProvider<?>> implements BatchService {

@Parameter
private LogService log;

@Parameter
private CommandService commandService;

/**
* Returns true if {@code type} is a {@link File}.
*/
@Override
public boolean supports(Class<?> type) {
return type.isAssignableFrom(File.class);
public boolean supportsItem(ModuleItem<?> moduleItem) {
BatchInputProvider<?> handler = getHandler(new BatchInput(File.class, moduleItem));
if (handler == null) {
return false;
}
return handler.canProvide(mo 10000 duleItem);
}

@SuppressWarnings("unchecked")
@Override
public <I> void fillInput(Module module, ModuleItem<?> moduleItem, I inputObject) {
BatchInputProvider<File> handler = (BatchInputProvider<File>) getHandler(new BatchInput(File.class, moduleItem));
if (handler == null) {
log.error("No handler found for input: " + moduleItem.getName());
return;
}
handler.populateInput(module, moduleItem, (File) inputObject);
}

@Override
Expand All @@ -37,10 +55,30 @@ public void run(ModuleInfo moduleInfo) {
log.error("No compatible inputs (of type File) found.");
return;
}

// 1) get input ModuleItems
// 2) choose ModuleItem
// 3) get suitable BatchInputProvider
// 4) get Iterator for ModuleItem

//for (getHandler(new BatchInput(File.class, moduleItem)).iterate(fileList)) {
//}

// Call ModuleBatchProcessor with input moduleInfo
HashMap<String, Object> inputMap = new HashMap<>();
inputMap.put("moduleInfo", moduleInfo);
commandService.run(ModuleBatchProcessor.class, true, inputMap);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Class<BatchInputProvider<?>> getPluginType() {
return (Class) BatchInputProvider.class;
}

@Override
public Class<BatchInput> getType() {
return BatchInput.class;
}

}
15 changes: 9 additions & 6 deletions src/main/java/org/scijava/batch/ModuleBatchProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.scijava.widget.FileWidget;

@Plugin(type = Command.class, label = "Choose batch processing parameters", initializer = "initInputChoice")
public class ModuleBatchProcessor extends DynamicCommand {
public class ModuleBatchProcessor<T> extends DynamicCommand {
@Parameter
private ModuleService moduleService;

Expand Down Expand Up @@ -66,6 +66,8 @@ protected void initInputChoice() {
} else if (compatibleInputs.size() > 1) {
choiceInput.setChoices(compatibleInputs.stream()
.map(ModuleItem::getName).collect(Collectors.toList()));
} else {
log.error("No compatible inputs found. Unable to initialize input choice.");
}
}

Expand All @@ -74,7 +76,7 @@ protected void initInputChoice() {
@Override
public void run() {
// mark inputChoice as resolved, then harvest script parameters (i.e. run)
ModuleItem<File> fileInput = moduleInfo.getInput(inputChoice, File.class);
ModuleItem<?> inputModuleItem = moduleInfo.getInput(inputChoice);
// TODO check if conversion needed?
Module scriptModule = moduleService.createModule(moduleInfo);
scriptModule.resolveInput(inputChoice);
Expand All @@ -89,8 +91,8 @@ public void run() {
scriptModule.resolveOutput(outputKey);
}

for (File file: inputFileList) {
if(!processFile(scriptModule, fileInput, file)) {
for (File file : inputFileList) {
if(!processFile(scriptModule, inputModuleItem, file)) {
log.warn("Terminating batch process.");
break; // end for loop
}
Expand All @@ -105,8 +107,9 @@ public void run() {
// -- Helper methods --

@SuppressWarnings("unchecked")
private boolean processFile(Module module, ModuleItem<File> fileInput, File file) {
fileInput.setValue(module, file);
private boolean processFile(Module module, ModuleItem<?> inputModuleItem, File file) {
batchService.fillInput(module, inputModuleItem, file);
//fileInput.setValue(module, file);
outputTable.appendRow(file.getName());

Future<Module> instance = moduleService.run(module, true);
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/org/scijava/batch/input/BatchInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.scijava.batch.input;

import java.lang.reflect.Type;

import org.scijava.module.ModuleItem;

/**
* Currency for use with {@link BatchInputProvider} methods
*
*
*
* @author Jan Eglinger
*
*/
public class BatchInput {
private final Type srcType;
private final ModuleItem<?> destItem;

public BatchInput(final Class<?> srcClass, final ModuleItem<?> destItem) {
this.srcType = srcClass;
this.destItem = destItem;
}

public BatchInput(final Type srcType, final ModuleItem<?> destItem) {
this.srcType = srcType;
this.destItem = destItem;
}

public Type sourceType() {
return srcType;
}

public ModuleItem<?> moduleItem() {
return destItem;
}
}
32 changes: 32 additions & 0 deletions src/main/java/org/scijava/batch/input/BatchInputProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

package org.scijava.batch.input;

import org.scijava.module.Module;
import org.scijava.module.ModuleItem;
import org.scijava.plugin.HandlerPlugin;

public interface BatchInputProvider<I> extends HandlerPlugin<BatchInput> {

@Override
default public Class<BatchInput> getType() {
return BatchInput.class;
}

/**
* Check if a given {@code ModuleItem} can be populated by this input provider.
* Implementations should make sure to not only match the type of objects, but
* also respect possible widget style settings, such as files, directories,
* image files only, etc.
*
* @param moduleItem
* the input item that needs to be populated
* @return true if this input provider can provide suitable objects
*/
public boolean canProvide(ModuleItem<?> moduleItem);

public void populateInput(Module module, ModuleItem<?> moduleItem, I inputObject);

public String getTargetWidgetStyle(ModuleItem<?> moduleItem);

// public Iterable<O> iterate(Collection<I> fileList);
}
65 changes: 65 additions & 0 deletions src/main/java/org/scijava/batch/input/FileBatchInputProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

package org.scijava.batch.input;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;

import org.scijava.module.Module;
import org.scijava.module.ModuleItem;
import org.scijava.plugin.AbstractHandlerPlugin;
import org.scijava.plugin.Plugin;
import org.scijava.widget.FileListWidget;
import org.scijava.widget.FileWidget;

@Plugin(type = BatchInputProvider.class)
public class FileBatchInputProvider extends AbstractHandlerPlugin<BatchInput> implements
BatchInputProvider<File>
{
@Override
public boolean supports(BatchInput input) {
return canProvide(input.moduleItem());
}

@Override
public boolean canProvide(ModuleItem<?> item) {
// we can't provide inputs for saving files
return item.getType() == File.class && !hasStyle(item, FileWidget.SAVE_STYLE);
}

@SuppressWarnings("unchecked")
@Override
public void populateInput(Module module, ModuleItem<?> moduleItem, File inputObject) {
((ModuleItem<File>)moduleItem).setValue(module, inputObject);
}

@Override
public String getTargetWidgetStyle(ModuleItem<?> item) {
ArrayList<String> targetStyles = new ArrayList<>();

if (hasStyle(item, FileWidget.DIRECTORY_STYLE)) {
targetStyles.add(FileListWidget.DIRECTORIES_ONLY);
} else {
targetStyles.add(FileListWidget.FILES_ONLY);
}

// extensions?
String widgetStyle = item.getWidgetStyle();
if (widgetStyle != null) {
String[] styles = widgetStyle.trim().split("\\s*,\\s*");
for (String s : styles) {
if (s.startsWith("extensions")) { // TODO: use new constant from FileListWidget
targetStyles.add(s);
}
}
}
return String.join(",", targetStyles);
}

private boolean hasStyle(ModuleItem<?> item, String style) {
String widgetStyle = item.getWidgetStyle();
if (widgetStyle == null) return false;
return Arrays.asList(widgetStyle.trim().split("\\s*,\\s*"))
.contains(style);
}
}
Loading
0