org.scijava.command.DefaultCommandService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scijava-common Show documentation
Show all versions of scijava-common Show documentation
SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.
/*
* #%L
* SciJava Common shared library for SciJava software.
* %%
* Copyright (C) 2009 - 2016 Board of Regents of the University of
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
* Institute of Molecular Cell Biology and Genetics.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.scijava.command;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import org.scijava.event.EventHandler;
import org.scijava.event.EventService;
import org.scijava.log.LogService;
import org.scijava.module.ModuleService;
import org.scijava.plugin.AbstractPTService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.PluginInfo;
import org.scijava.plugin.PluginService;
import org.scijava.plugin.SciJavaPlugin;
import org.scijava.plugin.event.PluginsAddedEvent;
import org.scijava.plugin.event.PluginsRemovedEvent;
import org.scijava.service.Service;
import org.scijava.util.ListUtils;
/**
* Default service for working with {@link Command}s. Available commands are
* obtained from the plugin service. Loading of the actual command classes can
* be deferred until a particular command's first execution.
*
* @author Curtis Rueden
* @see Command
*/
@Plugin(type = Service.class)
public class DefaultCommandService extends AbstractPTService implements
CommandService
{
@Parameter
private LogService log;
@Parameter
private EventService eventService;
@Parameter
private PluginService pluginService;
@Parameter
private ModuleService moduleService;
/** Mapping from vanilla plugin metadata to command metadata objects. */
private HashMap, CommandInfo> commandMap;
// -- CommandService methods --
@Override
public EventService eventService() {
return eventService;
}
@Override
public PluginService pluginService() {
return pluginService;
}
@Override
public ModuleService moduleService() {
return moduleService;
}
@Override
public List getCommands() {
return getCommandsOfType(Command.class);
}
@Override
public List getCommands(
final List> plugins)
{
final List commands = getCommandsUnknown(downcast(plugins));
return commands;
}
@Override
public List getCommandsOfType(
final Class type)
{
return getCommands(pluginService.getPluginsOfType(type));
}
@Override
public CommandInfo getCommand(
final Class commandClass)
{
return ListUtils.first(getCommandsOfClass(commandClass));
}
@Override
public CommandInfo getCommand(final String className) {
return ListUtils.first(getCommandsOfClass(className));
}
@Override
public List getCommandsOfClass(
final Class commandClass)
{
final List> plugins =
pluginService.getPluginsOfClass(commandClass, Command.class);
final List commands = getCommands(plugins);
return commands;
}
@Override
public List getCommandsOfClass(final String className)
{
final List> plugins =
pluginService.getPluginsOfClass(className);
final List commands = getCommandsUnknown(downcast(plugins));
return commands;
}
@Override
public Future run(final String className,
final boolean process, final Object... inputs)
{
return run(getOrCreate(className), process, inputs);
}
@Override
public Future run(final String className,
final boolean process, final Map inputMap)
{
return run(getOrCreate(className), process, inputMap);
}
@Override
public Future run(
final Class commandClass, final boolean process, final Object... inputs)
{
return run(getOrCreate(commandClass), process, inputs);
}
@Override
public Future run(
final Class commandClass, final boolean process,
final Map inputMap)
{
return run(getOrCreate(commandClass), process, inputMap);
}
@Override
public Future run(final CommandInfo info,
final boolean process, final Object... inputs)
{
@SuppressWarnings({ "rawtypes", "unchecked" })
final Future future =
(Future) moduleService.run(info, process, inputs);
return future;
}
@Override
public Future run(final CommandInfo info,
final boolean process, final Map inputMap)
{
@SuppressWarnings({ "rawtypes", "unchecked" })
final Future future =
(Future) moduleService.run(info, process, inputMap);
return future;
}
// -- PTService methods --
@Override
public Class getPluginType() {
return Command.class;
}
// -- Service methods --
@Override
public void initialize() {
commandMap = new HashMap<>();
// inform the module service of available commands
final List> plugins =
pluginService.getPluginsOfType(Command.class);
addCommands(plugins);
}
// -- Event handlers --
@EventHandler
protected void onEvent(final PluginsRemovedEvent event) {
removeCommands(event.getItems());
}
@EventHandler
protected void onEvent(final PluginsAddedEvent event) {
final ArrayList> commands =
new ArrayList<>();
findCommandPlugins(event.getItems(), commands);
addCommands(commands);
}
// -- Helper methods --
/**
* Gets a {@link CommandInfo} for the given class name, creating a new one if
* none are registered with the service.
*/
private CommandInfo getOrCreate(final String className) {
final CommandInfo command = getCommand(className);
if (command != null) return command;
return new CommandInfo(className);
}
/**
* Gets a {@link CommandInfo} for the given class, creating a new one if
* none are registered with the service.
*/
private CommandInfo getOrCreate(
final Class commandClass)
{
final CommandInfo command = getCommand(commandClass);
if (command != null) return command;
return new CommandInfo(commandClass);
}
/** Adds new commands to the module service. */
private void addCommands(final List> plugins) {
// extract commands from the list of plugins
final List commands = new ArrayList<>();
for (final PluginInfo info : plugins) {
final CommandInfo commandInfo = wrapAsCommand(info);
commands.add(commandInfo);
// record association between plugin info and derived command info
commandMap.put(info, commandInfo);
}
// add extracted commands to the module service
moduleService.addModules(commands);
}
/** Removes old commands from the module service. */
private void removeCommands(final List> plugins) {
final List commands = getCommandsUnknown(plugins);
for (final CommandInfo info : commands) {
// clear association between plugin info and derived command info
commandMap.remove(info);
}
// remove extracted commands from the module service
moduleService.removeModules(commands);
}
/**
* Gets the command corresponding to each plugin on the given list. The
* linkage is obtained from the {@link #commandMap}.
*/
private List getCommandsUnknown(
final List> plugins)
{
final List commands = new ArrayList<>();
for (final PluginInfo> info : plugins) {
final CommandInfo commandInfo = commandMap.get(info);
if (commandInfo == null) continue;
commands.add(commandInfo);
}
return commands;
}
/**
* Transfers command plugins from the source list to the destination list.
*
* @param srcList The list to scan for matching plugins.
* @param destList The list to which matching plugins are added.
*/
private void findCommandPlugins(final List extends PluginInfo>> srcList,
final List> destList)
{
for (final PluginInfo> info : srcList) {
if (isCommand(info)) {
@SuppressWarnings("unchecked")
final PluginInfo match = (PluginInfo) info;
destList.add(match);
}
}
}
/** Determines whether the given plugin is a command. */
private boolean isCommand(final PluginInfo> info) {
return Command.class.isAssignableFrom(info.getPluginType());
}
/** Converts the given plugin into a command. */
private CommandInfo wrapAsCommand(final PluginInfo pluginInfo) {
if (pluginInfo instanceof CommandInfo) {
// plugin info is already a command info
return (CommandInfo) pluginInfo;
}
// wrap the plugin's metadata in a command info
return new CommandInfo(pluginInfo);
}
/** A HACK for downcasting a list of plugins. */
private List> downcast(
final List> plugins)
{
// HACK: It seems that neither List> nor
// List> are usable to fulfill a method argument
// of type List>. Probably something relating to (lack of)
// covariance of generics that I am too stupid to understand.
// So we brute force it!
@SuppressWarnings({ "rawtypes", "unchecked" })
final List> typedPlugins = (List) plugins;
return typedPlugins;
}
}