All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.command.DefaultCommandService Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 2.99.0
Show newest version
/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2017 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> 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;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy