Skip to content

Commit

Permalink
Merge branch 'headless-outputs'
Browse files Browse the repository at this point in the history
This branch improves the headless UI to emit output values when
executing modules headless. For example, the following script:

    // @input String name
    // @input int age
    // @output String greeting
    greeting = "Hello, " + name + ". You are " + age + " years old."

now executes as follows:

    $ imagej -- --ij2 --headless --run hello.groovy 'name="Curtis",age=37'
    [INFO] greeting = Hello, Curtis. You are 37 years old.

Previously, the script would execute silently with no output;
headless scripts needed to use print statements to emit anything.
  • Loading branch information
ctrueden committed Jan 10, 2017
2 parents 2340fb5 + 8afe5da commit 36f49a8
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 77 deletions.
3 changes: 2 additions & 1 deletion src/main/java/org/scijava/AbstractGateway.java
Expand Up @@ -98,7 +98,8 @@ public void launch(final String... args) {
final int mainCount = main().execMains();

// display the user interface (NB: does not block)
if (mainCount == 0 && !ui().isHeadless()) ui().showUI();
// NB: When running headless, the HeadlessUI will be used.
if (mainCount == 0) ui().showUI();

if (ui().isHeadless()) {
// now that CLI processing/execution is done, we can shut down
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/org/scijava/ui/DefaultUIService.java
Expand Up @@ -62,7 +62,7 @@
import org.scijava.ui.DialogPrompt.OptionType;
import org.scijava.ui.DialogPrompt.Result;
import org.scijava.ui.event.UIShownEvent;
import org.scijava.ui.headlessUI.HeadlessUI;
import org.scijava.ui.headless.HeadlessUI;
import org.scijava.ui.viewer.DisplayViewer;

/**
Expand Down Expand Up @@ -176,7 +176,6 @@ public boolean isVisible(final String name) {
return ui.isVisible();
}


@Override
public void setHeadless(final boolean headless) {
System.setProperty("java.awt.headless", String.valueOf(headless));
Expand Down
66 changes: 66 additions & 0 deletions src/main/java/org/scijava/ui/headless/HeadlessDisplayViewer.java
@@ -0,0 +1,66 @@
/*
* #%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.ui.headless;

import org.scijava.display.Display;
import org.scijava.plugin.Plugin;
import org.scijava.ui.UserInterface;
import org.scijava.ui.viewer.AbstractDisplayViewer;
import org.scijava.ui.viewer.DisplayViewer;
import org.scijava.ui.viewer.DisplayWindow;
import org.scijava.util.ListUtils;

/**
* A display viewer used when running headless.
*
* @author Curtis Rueden
*/
@Plugin(type = DisplayViewer.class)
public class HeadlessDisplayViewer extends AbstractDisplayViewer<Object> {

@Override
public boolean isCompatible(final UserInterface ui) {
return ui instanceof HeadlessUI;
}

@Override
public void view(final DisplayWindow w, final Display<?> d) {
if (log() == null) return;
log().info(d.getName() + " = " + ListUtils.string(d, false));
}

@Override
public boolean canView(Display<?> d) {
return true;
}

}
124 changes: 124 additions & 0 deletions src/main/java/org/scijava/ui/headless/HeadlessUI.java
@@ -0,0 +1,124 @@
/*-
* #%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.ui.headless;

import java.io.File;

import org.scijava.Priority;
import org.scijava.display.Display;
import org.scijava.log.LogService;
import org.scijava.plugin.AbstractRichPlugin;
import org.scijava.plugin.Plugin;
import org.scijava.ui.DialogPrompt;
import org.scijava.ui.UserInterface;
import org.scijava.ui.viewer.DisplayWindow;
import org.scijava.util.ListUtils;

/**
* A no-op user interface used when the application is running headless.
* <p>
* Most operations do nothing. Attempting to show an object via one of the
* {@link #show} methods logs the object via the {@link LogService}.
* </p>
*
* @author Richard Domander (Royal Veterinary College, London)
* @author Curtis Rueden
*/
@Plugin(type = UserInterface.class, name = HeadlessUI.NAME,
priority = Priority.VERY_LOW_PRIORITY)
public class HeadlessUI extends AbstractRichPlugin implements UserInterface {

public static final String NAME = "headless";

private boolean visible;

@Override
public void show() {
visible = true;
}

@Override
public boolean isVisible() {
return visible;
}

@Override
public void show(final String name, final Object o) {
// NB: Rather than creating a Display, let's just log it.
log().info(name + " = " + o);
}

@Override
public void show(final Display<?> display) {
// NB: Rather than looking for a DisplayViewer, let's just log it.
log().info(display.getName() + " = " + ListUtils.string(display, false));
}

@Override
public DisplayWindow createDisplayWindow(final Display<?> display) {
return null;
}

@Override
public DialogPrompt dialogPrompt(final String message, final String title,
final DialogPrompt.MessageType messageType,
final DialogPrompt.OptionType optionType)
{
return null;
}

@Override
public File chooseFile(final String title, final File file,
final String style)
{
return null;
}

@Override
public void showContextMenu(final String menuRoot, final Display<?> display,
final int x, final int y)
{}

@Override
public void saveLocation() {}

@Override
public void restoreLocation() {}

@Override
public boolean requiresEDT() {
return false;
}

@Override
public void dispose() {}
}
82 changes: 9 additions & 73 deletions src/main/java/org/scijava/ui/headlessUI/HeadlessUI.java
Expand Up @@ -31,79 +31,15 @@

package org.scijava.ui.headlessUI;

import java.io.File;

import org.scijava.Priority;
import org.scijava.display.Display;
import org.scijava.plugin.AbstractRichPlugin;
import org.scijava.plugin.Plugin;
import org.scijava.ui.DialogPrompt;
import org.scijava.ui.UserInterface;
import org.scijava.ui.viewer.DisplayWindow;

/**
* A "null object" UI implementation that can be returned when a UIService is
* running headless
*
* @author Richard Domander (Royal Veterinary College, London)
* @author Curtis Rueden
*/
@Plugin(type = UserInterface.class, name = HeadlessUI.NAME,
priority = Priority.VERY_LOW_PRIORITY)
public class HeadlessUI extends AbstractRichPlugin implements UserInterface {

/** @deprecated Use {@link org.scijava.ui.headless.HeadlessUI} instead. */
@Deprecated
public class HeadlessUI extends org.scijava.ui.headless.HeadlessUI {

/**
* @deprecated Use {@link org.scijava.ui.headless.HeadlessUI#NAME} instead.
*/
@Deprecated
@SuppressWarnings("hiding")
public static final String NAME = "headless";

@Override
public void show() {}

@Override
public boolean isVisible() {
return false;
}

@Override
public void show(final String name, final Object o) {}

@Override
public void show(final Display<?> display) {}

@Override
public DisplayWindow createDisplayWindow(final Display<?> display) {
return null;
}

@Override
public DialogPrompt dialogPrompt(final String message, final String title,
final DialogPrompt.MessageType messageType,
final DialogPrompt.OptionType optionType)
{
return null;
}

@Override
public File chooseFile(final String title, final File file,
final String style)
{
return null;
}

@Override
public void showContextMenu(final String menuRoot, final Display<?> display,
final int x, final int y)
{}

@Override
public void saveLocation() {}

@Override
public void restoreLocation() {}

@Override
public boolean requiresEDT() {
return false;
}

@Override
public void dispose() {}
}
65 changes: 65 additions & 0 deletions src/main/java/org/scijava/util/ListUtils.java
Expand Up @@ -53,4 +53,69 @@ public static <T> T first(final List<T> list) {
return list.get(0);
}

/**
* Converts the given list to a string.
* <p>
* The list elements will be separated by a comma then a space. The list will
* be enclosed in square brackets.
* </p>
*
* @param list The list to stringify.
* @see #string(List, String, String, String, boolean)
*/
public static String string(final List<?> list) {
return string(list, true);
}

/**
* Converts the given list to a string.
* <p>
* The list elements will be separated by a comma then a space. The list will
* be enclosed in square brackets unless it is a singleton with the
* {@code encloseSingletons} flag set to false.
* </p>
*
* @param list The list to stringify.
* @param encloseSingletons Whether to enclose singleton lists in brackets.
* @return The stringified list.
* @see #string(List, String, String, String, boolean)
*/
public static String string(final List<?> list,
final boolean encloseSingletons)
{
return string(list, "[", "]", ", ", encloseSingletons);
}

/**
* Converts the given list to a string.
* <p>
* The list elements will be comma-separated. It will be enclosed in square
* brackets unless the list is a singleton with the {@code encloseSingletons}
* flag set to false.
* </p>
*
* @param list The list to stringify.
* @param lDelimiter The left-hand symbol(s) in which to enclose the list.
* @param rDelimiter The right-hand symbol(s) in which to enclose the list.
* @param separator The symbol(s) to place in between each element.
* @param encloseSingletons Whether to enclose singleton lists inside the
* delimiter symbols.
* @return The stringified list.
*/
public static String string(final List<?> list, //
final String lDelimiter, final String rDelimiter, //
final String separator, final boolean encloseSingletons)
{
final boolean delimit = encloseSingletons || list.size() != 1;
final StringBuilder sb = new StringBuilder();
if (delimit) sb.append(lDelimiter);
boolean first = true;
for (final Object e : list) {
if (first) first = false;
else sb.append(separator);
sb.append(e);
}
if (delimit) sb.append(rDelimiter);
return sb.toString();
}
}
2 changes: 1 addition & 1 deletion src/test/java/org/scijava/ui/UIServiceTest.java
Expand Up @@ -42,7 +42,7 @@
import org.junit.Test;
import org.scijava.Context;
import org.scijava.display.Display;
import org.scijava.ui.headlessUI.HeadlessUI;
import org.scijava.ui.headless.HeadlessUI;
import org.scijava.ui.viewer.DisplayWindow;

/**
Expand Down

0 comments on commit 36f49a8

Please sign in to comment.