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

org.scijava.script.process.ParameterScriptProcessor Maven / Gradle / Ivy

/*
 * #%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, Max Planck
 * Institute of Molecular Cell Biology and Genetics, University of
 * Konstanz, and KNIME GmbH.
 * %%
 * 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.process;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.ScriptException;

import org.scijava.ItemIO;
import org.scijava.ItemVisibility;
import org.scijava.command.Command;
import org.scijava.convert.ConvertService;
import org.scijava.log.LogService;
import org.scijava.module.DefaultMutableModuleItem;
import org.scijava.parse.ParseService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.script.ScriptInfo;
import org.scijava.script.ScriptModule;
import org.scijava.script.ScriptService;

/**
 * A {@link ScriptProcessor} which parses the script's input and output
 * parameters from the script header.
 * 

* SciJava's scripting framework supports specifying @{@link Parameter}-style * inputs and outputs in a preamble. The format is a simplified version of the * Java @{@link Parameter} annotation syntax. The following syntaxes are * supported: *

*
    *
  • {@code #@ }
  • *
  • {@code #@ (=, ..., =) }
  • *
  • {@code #@ }
  • *
  • {@code #@ }
  • *
  • {@code #@(=, ..., =) * }
  • *
*

* Where: *

*
    *
  • {@code #@} - signals a special script processing instruction, so that the * parameter line is ignored by the script engine itself.
  • *
  • {@code } - one of {@code INPUT}, {@code OUTPUT}, or {@code BOTH}. *
  • *
  • {@code } - the name of the input or output variable.
  • *
  • {@code } - the Java {@link Class} of the variable, or * {@link Object} if none specified.
  • *
  • {@code } - an attribute key.
  • *
  • {@code } - an attribute value.
  • *
*

* See the @{@link Parameter} annotation for a list of valid attributes. *

*

* Here are a few examples: *

*
    *
  • {@code #@Dataset dataset}
  • *
  • {@code #@double(type=OUTPUT) result}
  • *
  • {@code #@both ImageDisplay display}
  • *
  • {@code #@input(persist=false, visibility=INVISIBLE) boolean verbose}
  • *
  • {@code #@output thing}
  • *
*

* Parameters will be parsed and filled just like @{@link Parameter}-annotated * fields in {@link Command}s. *

* * @author Curtis Rueden */ @Plugin(type = ScriptProcessor.class) public class ParameterScriptProcessor implements ScriptProcessor { @Parameter private ScriptService scriptService; @Parameter private ConvertService convertService; @Parameter private ParseService parser; @Parameter private LogService log; private ScriptInfo info; private boolean header; // -- ScriptProcessor methods -- @Override public void begin(final ScriptInfo scriptInfo) { info = scriptInfo; info.setReturnValueAppended(true); header = true; } @Override public String process(final String line) { // parse new-style parameters starting with @# anywhere in the script. if (line.matches("^#@.*")) { final int at = line.indexOf('@'); return process(line, line.substring(at + 1)); } // parse old-style parameters in the initial script header if (header) { // NB: Check if line contains an '@' with no prior alphameric // characters. This assumes that only non-alphanumeric characters can // be used as comment line markers. if (line.matches("^[^\\w]*@.*")) { final int at = line.indexOf('@'); return process(line, line.substring(at + 1)); } else if (line.matches(".*\\w.*")) header = false; } return line; } @Override public void end() { if (info.isReturnValueAppended()) { // add an output for the value returned by the script itself final HashMap attrs = new HashMap<>(); attrs.put("type", "OUTPUT"); addItem(ScriptModule.RETURN_VALUE, Object.class, attrs, false); } } // -- Helper methods -- private String process(final String line, final String param) { if (parseParam(param)) return ""; log.warn("Ignoring invalid parameter: " + param); return line; } private boolean parseParam(final String param) { final int lParen = param.indexOf("("); final int rParen = param.lastIndexOf(")"); if (rParen < lParen) return false; if (lParen < 0) return parseParam(param, parseAttrs("()")); final String cutParam = param.substring(0, lParen) + param.substring(rParen + 1); final String attrs = param.substring(lParen + 1, rParen); return parseParam(cutParam, parseAttrs(attrs)); } private boolean parseParam(final String param, final Map attrs) { final String[] tokens = param.trim().split("[ \t\n]+"); if (tokens.length < 1) return false; final String typeName, varName; final String maybeIOType = tokens[0].toUpperCase(); if (isIOType(maybeIOType)) { if (tokens.length == 2) { // typeName = "Object"; varName = tokens[1]; } else if (tokens.length == 3) { // typeName = tokens[1]; varName = tokens[2]; } else return false; attrs.put("type", maybeIOType); } else { // assume syntax: if (tokens.length < 2) return false; typeName = tokens[0]; varName = tokens[1]; } try { final Class type = scriptService.lookupClass(typeName); addItem(varName, type, attrs, true); } catch (final ScriptException exc) { log.warn("Invalid class: " + typeName, exc); return false; } if (ScriptModule.RETURN_VALUE.equals(varName)) { // NB: The return value variable is declared as an explicit parameter. // So we should not append the return value as an extra output. info.setReturnValueAppended(false); } return true; } /** Parses a comma-delimited list of {@code key=value} pairs into a map. */ private Map parseAttrs(final String attrs) { return parser.parse(attrs, false).asMap(); } private boolean isIOType(final String token) { return convertService.convert(token.toUpperCase(), ItemIO.class) != null; } private void addItem(final String name, final Class type, final Map attrs, final boolean explicit) { final DefaultMutableModuleItem item = new DefaultMutableModuleItem<>(info, name, type); for (final String key : attrs.keySet()) { final Object value = attrs.get(key); assignAttribute(item, key, value); } if (item.isInput()) info.registerInput(item); if (item.isOutput()) { info.registerOutput(item); // NB: Only append the return value as an extra // output when no explicit outputs are declared. if (explicit) info.setReturnValueAppended(false); } } private void assignAttribute(final DefaultMutableModuleItem item, final String k, final Object v) { // CTR: There must be an easier way to do this. // Just compile the thing using javac? Or parse via javascript, maybe? if (is(k, "callback")) item.setCallback(as(v, String.class)); else if (is(k, "choices")) item.setChoices(asList(v, item.getType())); else if (is(k, "columns")) item.setColumnCount(as(v, int.class)); else if (is(k, "description")) item.setDescription(as(v, String.class)); else if (is(k, "initializer")) item.setInitializer(as(v, String.class)); else if (is(k, "validater")) item.setValidater(as(v, String.class)); else if (is(k, "type")) item.setIOType(as(v, ItemIO.class)); else if (is(k, "label")) item.setLabel(as(v, String.class)); else if (is(k, "max")) item.setMaximumValue(as(v, item.getType())); else if (is(k, "min")) item.setMinimumValue(as(v, item.getType())); else if (is(k, "name")) item.setName(as(v, String.class)); else if (is(k, "persist")) item.setPersisted(as(v, boolean.class)); else if (is(k, "persistKey")) item.setPersistKey(as(v, String.class)); else if (is(k, "required")) item.setRequired(as(v, boolean.class)); else if (is(k, "softMax")) item.setSoftMaximum(as(v, item.getType())); else if (is(k, "softMin")) item.setSoftMinimum(as(v, item.getType())); else if (is(k, "stepSize")) item.setStepSize(as(v, double.class)); else if (is(k, "style")) item.setWidgetStyle(as(v, String.class)); else if (is(k, "visibility")) item.setVisibility(as(v, ItemVisibility.class)); else if (is(k, "value")) item.setDefaultValue(as(v, item.getType())); else item.set(k, v.toString()); } /** Super terse comparison helper method. */ private boolean is(final String key, final String desired) { return desired.equalsIgnoreCase(key); } /** Super terse conversion helper method. */ private T as(final Object v, final Class type) { final T converted = convertService.convert(v, type); if (converted != null) return converted; // NB: Attempt to convert via string. // This is useful in cases where a weird type of object came back // (e.g., org.scijava.parse.eval.Unresolved), but which happens to have a // nice string representation which ultimately is expressible as the type. return convertService.convert(v.toString(), type); } private List asList(final Object v, final Class type) { final ArrayList result = new ArrayList<>(); final List list = as(v, List.class); for (final Object item : list) { result.add(as(item, type)); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy