
org.scijava.script.DefaultScriptService 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 both 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.script;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import javax.script.ScriptException;
import org.scijava.Context;
import org.scijava.Gateway;
import org.scijava.InstantiableException;
import org.scijava.MenuPath;
import org.scijava.Priority;
import org.scijava.command.CommandService;
import org.scijava.event.EventHandler;
import org.scijava.log.LogService;
import org.scijava.module.Module;
import org.scijava.module.ModuleService;
import org.scijava.object.LazyObjects;
import org.scijava.parse.ParseService;
import org.scijava.plugin.AbstractSingletonService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.PluginInfo;
import org.scijava.plugin.PluginService;
import org.scijava.service.Service;
import org.scijava.service.event.ServicesLoadedEvent;
import org.scijava.util.AppUtils;
import org.scijava.util.ClassUtils;
import org.scijava.util.ColorRGB;
import org.scijava.util.ColorRGBA;
/**
* Default service for working with scripting languages.
*
* @author Johannes Schindelin
* @author Curtis Rueden
*/
@Plugin(type = Service.class, priority = Priority.HIGH_PRIORITY)
public class DefaultScriptService extends
AbstractSingletonService implements ScriptService
{
@Parameter
private PluginService pluginService;
@Parameter
private ModuleService moduleService;
@Parameter
private CommandService commandService;
@Parameter
private ParseService parser;
@Parameter
private LogService log;
/** Index of registered scripting languages. */
private ScriptLanguageIndex scriptLanguageIndex;
/** List of directories to scan for scripts. */
private ArrayList scriptDirs;
/** Menu prefix to use for each script directory, if any. */
private HashMap menuPrefixes;
/** Index of available scripts, by script file. */
private HashMap scripts;
/** Table of short type names to associated {@link Class}. */
private HashMap> aliasMap;
// -- ScriptService methods - scripting languages --
@Override
public ScriptLanguageIndex getIndex() {
return scriptLanguageIndex();
}
@Override
public List getLanguages() {
return new ArrayList(getIndex());
}
@Override
public ScriptLanguage getLanguageByExtension(final String extension) {
return getIndex().getByExtension(extension);
}
@Override
public ScriptLanguage getLanguageByName(final String name) {
return getIndex().getByName(name);
}
// -- ScriptService methods - scripts --
@Override
public List getScriptDirectories() {
return Collections.unmodifiableList(scriptDirs());
}
@Override
public MenuPath getMenuPrefix(final File scriptDirectory) {
return menuPrefixes().get(scriptDirectory);
}
@Override
public void addScriptDirectory(final File scriptDirectory) {
scriptDirs().add(scriptDirectory);
}
@Override
public void addScriptDirectory(final File scriptDirectory,
final MenuPath menuPrefix)
{
scriptDirs().add(scriptDirectory);
menuPrefixes().put(scriptDirectory, menuPrefix);
}
@Override
public void removeScriptDirectory(final File scriptDirectory) {
scriptDirs().remove(scriptDirectory);
}
@Override
public Collection getScripts() {
return Collections.unmodifiableCollection(scripts().values());
}
@Override
public ScriptInfo getScript(final File scriptFile) {
return getOrCreate(scriptFile);
}
@Override
public Future run(final File file, final boolean process,
final Object... inputs)
{
return run(getOrCreate(file), process, inputs);
}
@Override
public Future run(final File file, final boolean process,
final Map inputMap)
{
return run(getOrCreate(file), process, inputMap);
}
@Override
public Future run(final String path, final String script,
final boolean process, final Object... inputs)
{
return run(path, new StringReader(script), process, inputs);
}
@Override
public Future run(final String path, final String script,
final boolean process, final Map inputMap)
{
return run(path, new StringReader(script), process, inputMap);
}
@Override
public Future run(final String path, final Reader reader,
final boolean process, final Object... inputs)
{
return run(new ScriptInfo(getContext(), path, reader), process, inputs);
}
@Override
public Future run(final String path, final Reader reader,
final boolean process, final Map inputMap)
{
return run(new ScriptInfo(getContext(), path, reader), process, inputMap);
}
@Override
public Future run(final ScriptInfo info, final boolean process,
final Object... inputs)
{
return cast(moduleService.run(info, process, inputs));
}
@Override
public Future run(final ScriptInfo info, final boolean process,
final Map inputMap)
{
return cast(moduleService.run(info, process, inputMap));
}
@Override
public boolean canHandleFile(final File file) {
return getIndex().canHandleFile(file);
}
@Override
public boolean canHandleFile(final String fileName) {
return getIndex().canHandleFile(fileName);
}
@Override
public void addAlias(final Class> type) {
addAlias(type.getSimpleName(), type);
}
@Override
public void addAlias(final String alias, final Class> type) {
aliasMap().put(alias, type);
}
@Override
public synchronized Class> lookupClass(final String alias)
throws ScriptException
{
final Class> type = aliasMap().get(alias);
if (type != null) return type;
try {
final Class> c = ClassUtils.loadClass(alias, false);
aliasMap().put(alias, c);
return c;
}
catch (final IllegalArgumentException exc) {
final ScriptException se = new ScriptException("Unknown type: " + alias);
se.initCause(exc);
throw se;
}
}
// -- PTService methods --
@Override
public Class getPluginType() {
return ScriptLanguage.class;
}
// -- Service methods --
@Override
public void initialize() {
super.initialize();
// add scripts to the module index... only when needed!
moduleService.getIndex().addLater(new LazyObjects() {
@Override
public Collection get() {
return scripts().values();
}
});
}
// -- Event handlers --
@EventHandler
private void
onEvent(@SuppressWarnings("unused") final ServicesLoadedEvent evt)
{
// NB: Add service type aliases after all services have joined the context.
for (final Service service : getContext().getServiceIndex()) {
addAliases(aliasMap(), service.getClass());
}
}
// -- Helper methods - lazy initialization --
/** Gets {@link #scriptLanguageIndex}, initializing if needed. */
private ScriptLanguageIndex scriptLanguageIndex() {
if (scriptLanguageIndex == null) initScriptLanguageIndex();
return scriptLanguageIndex;
}
/** Gets {@link #scriptDirs}, initializing if needed. */
private List scriptDirs() {
if (scriptDirs == null) initScriptDirs();
return scriptDirs;
}
/** Gets {@link #menuPrefixes}, initializing if needed. */
private HashMap menuPrefixes() {
if (menuPrefixes == null) initMenuPrefixes();
return menuPrefixes;
}
/** Gets {@link #scripts}, initializing if needed. */
private HashMap scripts() {
if (scripts == null) initScripts();
return scripts;
}
/** Gets {@link #aliasMap}, initializing if needed. */
private HashMap> aliasMap() {
if (aliasMap == null) initAliasMap();
return aliasMap;
}
/** Initializes {@link #scriptLanguageIndex}. */
private synchronized void initScriptLanguageIndex() {
if (scriptLanguageIndex != null) return; // already initialized
final ScriptLanguageIndex index = new ScriptLanguageIndex(log);
// add ScriptLanguage plugins
for (final ScriptLanguage language : getInstances()) {
index.add(language, true);
}
scriptLanguageIndex = index;
}
/** Initializes {@link #scriptDirs}. */
private synchronized void initScriptDirs() {
if (scriptDirs != null) return;
final ArrayList dirs = new ArrayList();
// append default script directories
final File baseDir = AppUtils.getBaseDirectory(getClass()); //FIXME
dirs.add(new File(baseDir, "scripts"));
// append additional script directories from system property
final String scriptsPath = System.getProperty(SCRIPTS_PATH_PROPERTY);
if (scriptsPath != null) {
for (final String dir : scriptsPath.split(File.pathSeparator)) {
dirs.add(new File(dir));
}
}
scriptDirs = dirs;
}
/** Initializes {@link #menuPrefixes}. */
private synchronized void initMenuPrefixes() {
if (menuPrefixes != null) return;
menuPrefixes = new HashMap();
}
/** Initializes {@link #scripts}. */
private synchronized void initScripts() {
if (scripts != null) return; // already initialized
final HashMap map = new HashMap();
final ArrayList scriptList = new ArrayList();
new ScriptFinder(this).findScripts(scriptList);
for (final ScriptInfo info : scriptList) {
map.put(asFile(info.getPath()), info);
}
scripts = map;
}
/** Initializes {@link #aliasMap}. */
private synchronized void initAliasMap() {
if (aliasMap != null) return; // already initialized
final HashMap> map = new HashMap>();
// primitives
addAliases(map, boolean.class, byte.class, char.class, double.class,
float.class, int.class, long.class, short.class);
// primitive wrappers
addAliases(map, Boolean.class, Byte.class, Character.class, Double.class,
Float.class, Integer.class, Long.class, Short.class);
// built-in types
addAliases(map, Context.class, BigDecimal.class, BigInteger.class,
ColorRGB.class, ColorRGBA.class, File.class, String.class);
// gateway types
final List> gatewayPlugins =
pluginService.getPluginsOfType(Gateway.class);
for (final PluginInfo info : gatewayPlugins) {
try {
addAliases(map, info.loadClass());
}
catch (final InstantiableException exc) {
log.warn("Ignoring invalid gateway: " + info.getClassName(), exc);
}
}
aliasMap = map;
}
private void addAliases(final HashMap> map,
final Class>... types)
{
for (final Class> type : types) {
addAlias(map, type);
}
}
private void
addAlias(final HashMap> map, final Class> type)
{
if (type == null) return;
map.put(type.getSimpleName(), type);
// NB: Recursively add supertypes.
addAlias(map, type.getSuperclass());
addAliases(map, type.getInterfaces());
}
// -- Helper methods - run --
/**
* Gets a {@link ScriptInfo} for the given file, creating a new one if none
* are registered with the service.
*/
private ScriptInfo getOrCreate(final File file) {
final ScriptInfo info = scripts().get(file);
if (info != null) return info;
return new ScriptInfo(getContext(), file);
}
private File asFile(final String path) {
final File file = new File(path);
try {
return file.getCanonicalFile();
}
catch (final IOException exc) {
log.warn(exc);
return file.getAbsoluteFile();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Future cast(final Future future) {
return (Future) future;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy