Skip to content

Commit 36f49a8

Browse files
committed
Merge branch 'headless-outputs'
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.
2 parents 2340fb5 + 8afe5da commit 36f49a8

File tree

7 files changed

+268
-77
lines changed

7 files changed

+268
-77
lines changed

src/main/java/org/scijava/AbstractGateway.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ public void launch(final String... args) {
9898
final int mainCount = main().execMains();
9999

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

103104
if (ui().isHeadless()) {
104105
// now that CLI processing/execution is done, we can shut down

src/main/java/org/scijava/ui/DefaultUIService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
import org.scijava.ui.DialogPrompt.OptionType;
6363
import org.scijava.ui.DialogPrompt.Result;
6464
import org.scijava.ui.event.UIShownEvent;
65-
import org.scijava.ui.headlessUI.HeadlessUI;
65+
import org.scijava.ui.headless.HeadlessUI;
6666
import org.scijava.ui.viewer.DisplayViewer;
6767

6868
/**
@@ -176,7 +176,6 @@ public boolean isVisible(final String name) {
176176
return ui.isVisible();
177177
}
178178

179-
180179
@Override
181180
public void setHeadless(final boolean headless) {
182181
System.setProperty("java.awt.headless", String.valueOf(headless));
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2016 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.ui.headless;
33+
34+
import org.scijava.display.Display;
35+
import org.scijava.plugin.Plugin;
36+
import org.scijava.ui.UserInterface;
37+
import org.scijava.ui.viewer.AbstractDisplayViewer;
38+
import org.scijava.ui.viewer.DisplayViewer;
39+
import org.scijava.ui.viewer.DisplayWindow;
40+
import org.scijava.util.ListUtils;
41+
42+
/**
43+
* A display viewer used when running headless.
44+
*
45+
* @author Curtis Rueden
46+
*/
47+
@Plugin(type = DisplayViewer.class)
48+
public class HeadlessDisplayViewer extends AbstractDisplayViewer<Object> {
49+
50+
@Override
51+
public boolean isCompatible(final UserInterface ui) {
52+
return ui instanceof HeadlessUI;
53+
}
54+
55+
@Override
56+
public void view(final DisplayWindow w, final Display<?> d) {
57+
if (log() == null) return;
58+
log().info(d.getName() + " = " + ListUtils.string(d, false));
59+
}
60+
61+
@Override
62+
public boolean canView(Display<?> d) {
63+
return true;
64+
}
65+
66+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*-
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2016 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.ui.headless;
33+
34+
import java.io.File;
35+
36+
import org.scijava.Priority;
37+
import org.scijava.display.Display;
38+
import org.scijava.log.LogService;
39+
import org.scijava.plugin.AbstractRichPlugin;
40+
import org.scijava.plugin.Plugin;
41+
import org.scijava.ui.DialogPrompt;
42+
import org.scijava.ui.UserInterface;
43+
import org.scijava.ui.viewer.DisplayWindow;
44+
import org.scijava.util.ListUtils;
45+
46+
/**
47+
* A no-op user interface used when the application is running headless.
48+
* <p>
49+
* Most operations do nothing. Attempting to show an object via one of the
50+
* {@link #show} methods logs the object via the {@link LogService}.
51+
* </p>
52+
*
53+
* @author Richard Domander (Royal Veterinary College, London)
54+
* @author Curtis Rueden
55+
*/
56+
@Plugin(type = UserInterface.class, name = HeadlessUI.NAME,
57+
priority = Priority.VERY_LOW_PRIORITY)
58+
public class HeadlessUI extends AbstractRichPlugin implements UserInterface {
59+
60+
public static final String NAME = "headless";
61+
62+
private boolean visible;
63+
64+
@Override
65+
public void show() {
66+
visible = true;
67+
}
68+
69+
@Override
70+
public boolean isVisible() {
71+
return visible;
72+
}
73+
74+
@Override
75+
public void show(final String name, final Object o) {
76+
// NB: Rather than creating a Display, let's just log it.
77+
log().info(name + " = " + o);
78+
}
79+
80+
@Override
81+
public void show(final Display<?> display) {
82+
// NB: Rather than looking for a DisplayViewer, let's just log it.
83+
log().info(display.getName() + " = " + ListUtils.string(display, false));
84+
}
85+
86+
@Override
87+
public DisplayWindow createDisplayWindow(final Display<?> display) {
88+
return null;
89+
}
90+
91+
@Override
92+
public DialogPrompt dialogPrompt(final String message, final String title,
93+
final DialogPrompt.MessageType messageType,
94+
final DialogPrompt.OptionType optionType)
95+
{
96+
return null;
97+
}
98+
99+
@Override
100+
public File chooseFile(final String title, final File file,
101+
final String style)
102+
{
103+
return null;
104+
}
105+
106+
@Override
107+
public void showContextMenu(final String menuRoot, final Display<?> display,
108+
final int x, final int y)
109+
{}
110+
111+
@Override
112+
public void saveLocation() {}
113+
114+
@Override
115+
public void restoreLocation() {}
116+
117+
@Override
118+
public boolean requiresEDT() {
119+
return false;
120+
}
121+
122+
@Override
123+
public void dispose() {}
124+
}

src/main/java/org/scijava/ui/headlessUI/HeadlessUI.java

Lines changed: 9 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -31,79 +31,15 @@
3131

3232
package org.scijava.ui.headlessUI;
3333

34-
import java.io.File;
35-
36-
import org.scijava.Priority;
37-
import org.scijava.display.Display;
38-
import org.scijava.plugin.AbstractRichPlugin;
39-
import org.scijava.plugin.Plugin;
40-
import org.scijava.ui.DialogPrompt;
41-
import org.scijava.ui.UserInterface;
42-
import org.scijava.ui.viewer.DisplayWindow;
43-
44-
/**
45-
* A "null object" UI implementation that can be returned when a UIService is
46-
* running headless
47-
*
48-
* @author Richard Domander (Royal Veterinary College, London)
49-
* @author Curtis Rueden
50-
*/
51-
@Plugin(type = UserInterface.class, name = HeadlessUI.NAME,
52-
priority = Priority.VERY_LOW_PRIORITY)
53-
public class HeadlessUI extends AbstractRichPlugin implements UserInterface {
54-
34+
/** @deprecated Use {@link org.scijava.ui.headless.HeadlessUI} instead. */
35+
@Deprecated
36+
public class HeadlessUI extends org.scijava.ui.headless.HeadlessUI {
37+
38+
/**
39+
* @deprecated Use {@link org.scijava.ui.headless.HeadlessUI#NAME} instead.
40+
*/
41+
@Deprecated
42+
@SuppressWarnings("hiding")
5543
public static final String NAME = "headless";
5644

57-
@Override
58-
public void show() {}
59-
60-
@Override
61-
public boolean isVisible() {
62-
return false;
63-
}
64-
65-
@Override
66-
public void show(final String name, final Object o) {}
67-
68-
@Override
69-
public void show(final Display<?> display) {}
70-
71-
@Override
72-
public DisplayWindow createDisplayWindow(final Display<?> display) {
73-
return null;
74-
}
75-
76-
@Override
77-
public DialogPrompt dialogPrompt(final String message, final String title,
78-
final DialogPrompt.MessageType messageType,
79-
final DialogPrompt.OptionType optionType)
80-
{
81-
return null;
82-
}
83-
84-
@Override
85-
public File chooseFile(final String title, final File file,
86-
final String style)
87-
{
88-
return null;
89-
}
90-
91-
@Override
92-
public void showContextMenu(final String menuRoot, final Display<?> display,
93-
final int x, final int y)
94-
{}
95-
96-
@Override
97-
public void saveLocation() {}
98-
99-
@Override
100-
public void restoreLocation() {}
101-
102-
@Override
103-
public boolean requiresEDT() {
104-
return false;
105-
}
106-
107-
@Override
108-
public void dispose() {}
10945
}

src/main/java/org/scijava/util/ListUtils.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,69 @@ public static <T> T first(final List<T> list) {
5353
return list.get(0);
5454
}
5555

56+
/**
57+
* Converts the given list to a string.
58+
* <p>
59+
* The list elements will be separated by a comma then a space. The list will
60+
* be enclosed in square brackets.
61+
* </p>
62+
*
63+
* @param list The list to stringify.
64+
* @see #string(List, String, String, String, boolean)
65+
*/
66+
public static String string(final List<?> list) {
67+
return string(list, true);
68+
}
69+
70+
/**
71+
* Converts the given list to a string.
72+
* <p>
73+
* The list elements will be separated by a comma then a space. The list will
74+
* be enclosed in square brackets unless it is a singleton with the
75+
* {@code encloseSingletons} flag set to false.
76+
* </p>
77+
*
78+
* @param list The list to stringify.
79+
* @param encloseSingletons Whether to enclose singleton lists in brackets.
80+
* @return The stringified list.
81+
* @see #string(List, String, String, String, boolean)
82+
*/
83+
public static String string(final List<?> list,
84+
final boolean encloseSingletons)
85+
{
86+
return string(list, "[", "]", ", ", encloseSingletons);
87+
}
88+
89+
/**
90+
* Converts the given list to a string.
91+
* <p>
92+
* The list elements will be comma-separated. It will be enclosed in square
93+
* brackets unless the list is a singleton with the {@code encloseSingletons}
94+
* flag set to false.
95+
* </p>
96+
*
97+
* @param list The list to stringify.
98+
* @param lDelimiter The left-hand symbol(s) in which to enclose the list.
99+
* @param rDelimiter The right-hand symbol(s) in which to enclose the list.
100+
* @param separator The symbol(s) to place in between each element.
101+
* @param encloseSingletons Whether to enclose singleton lists inside the
102+
* delimiter symbols.
103+
* @return The stringified list.
104+
*/
105+
public static String string(final List<?> list, //
106+
final String lDelimiter, final String rDelimiter, //
107+
final String separator, final boolean encloseSingletons)
108+
{
109+
final boolean delimit = encloseSingletons || list.size() != 1;
110+
final StringBuilder sb = new StringBuilder();
111+
if (delimit) sb.append(lDelimiter);
112+
boolean first = true;
113+
for (final Object e : list) {
114+
if (first) first = false;
115+
else sb.append(separator);
116+
sb.append(e);
117+
}
118+
if (delimit) sb.append(rDelimiter);
119+
return sb.toString();
120+
}
56121
}

src/test/java/org/scijava/ui/UIServiceTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import org.junit.Test;
4343
import org.scijava.Context;
4444
import org.scijava.display.Display;
45-
import org.scijava.ui.headlessUI.HeadlessUI;
45+
import org.scijava.ui.headless.HeadlessUI;
4646
import org.scijava.ui.viewer.DisplayWindow;
4747

4848
/**

0 commit comments

Comments
 (0)