Skip to content

Commit 8365d95

Browse files
authored
Merge pull request #286 from imagejan/file-input-enhanced
Improved file inputs for scripts
2 parents 82ce585 + 1731a78 commit 8365d95

File tree

11 files changed

+330
-15
lines changed

11 files changed

+330
-15
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>org.scijava</groupId>
77
<artifactId>pom-scijava</artifactId>
8-
<version>13.1.0</version>
8+
<version>17.0.0</version>
99
<relativePath />
1010
</parent>
1111

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
7+
* Institute of Molecular Cell Biology and Genetics, University of
8+
* Konstanz, and KNIME GmbH.
9+
* %%
10+
* Redistribution and use in source and binary forms, with or without
11+
* modification, are permitted provided that the following conditions are met:
12+
*
13+
* 1. Redistributions of source code must retain the above copyright notice,
14+
* this list of conditions and the following disclaimer.
15+
* 2. Redistributions in binary form must reproduce the above copyright notice,
16+
* this list of conditions and the following disclaimer in the documentation
17+
* and/or other materials provided with the distribution.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
23+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+
* POSSIBILITY OF SUCH DAMAGE.
30+
* #L%
31+
*/
32+
33+
package org.scijava.convert;
34+
35+
import java.io.File;
36+
import java.util.ArrayList;
37+
import java.util.Arrays;
38+
import java.util.List;
39+
import java.util.stream.Collectors;
40+
41+
import org.scijava.Priority;
42+
import org.scijava.plugin.Plugin;
43+
import org.scijava.util.StringUtils;
44+
45+
/**
46+
* A collection of {@link Converter} plugins for going between {@link String},
47+
* {@link File} and {@code File[]}.
48+
*
49+
* @author Jan Eglinger
50+
* @author Curtis Rueden
51+
*/
52+
public class FileListConverters {
53+
// -- String to File (list) converters --
54+
55+
@Plugin(type = Converter.class, priority = Priority.NORMAL)
56+
public static class StringToFileConverter extends
57+
AbstractConverter<String, File>
58+
{
59+
60+
@SuppressWarnings("unchecked")
61+
@Override
62+
public <T> T convert(final Object src, final Class<T> dest) {
63+
return (T) new File((String) src);
64+
}
65+
66+
@Override
67+
public Class<File> getOutputType() {
68+
return File.class;
69+
}
70+
71+
@Override
72+
public Class<String> getInputType() {
73+
return String.class;
74+
}
75+
76+
}
77+
78+
@Plugin(type = Converter.class, priority = Priority.NORMAL)
79+
public static class StringToFileArrayConverter extends
80+
AbstractConverter<String, File[]>
81+
{
82+
83+
@SuppressWarnings("unchecked")
84+
@Override
85+
public <T> T convert(final Object src, final Class<T> dest) {
86+
final String[] tokens = StringUtils.splitUnquoted((String) src, ",");
87+
final List<File> fileList = new ArrayList<>();
88+
for (final String filePath : tokens) {
89+
fileList.add(new File(filePath.replaceAll("^\"|\"$", "")));
90+
}
91+
return (T) fileList.toArray(new File[fileList.size()]);
92+
}
93+
94+
@Override
95+
public Class<File[]> getOutputType() {
96+
return File[].class;
97+
}
98+
99+
@Override
100+
public Class<String> getInputType() {
101+
return String.class;
102+
}
103+
104+
}
105+
106+
// TODO add StringToFileListConverter
107+
108+
// -- File (list) to String converters --
109+
110+
@Plugin(type = Converter.class, priority = Priority.NORMAL)
111+
public static class FileToStringConverter extends
112+
AbstractConverter<File, String>
113+
{
114+
115+
@SuppressWarnings("unchecked")
116+
@Override
117+
public <T> T convert(final Object src, final Class<T> dest) {
118+
return (T) ((File) src).getAbsolutePath();
119+
}
120+
121+
@Override
122+
public Class<String> getOutputType() {
123+
return String.class;
124+
}
125+
126+
@Override
127+
public Class<File> getInputType() {
128+
return File.class;
129+
}
130+
131+
}
132+
133+
@Plugin(type = Converter.class, priority = Priority.NORMAL)
134+
public static class FileArrayToStringConverter extends
135+
AbstractConverter<File[], String>
136+
{
137+
138+
@SuppressWarnings("unchecked")
139+
@Override
140+
public <T> T convert(final Object src, final Class<T> dest) {
141+
final List<String> result = Arrays.asList((File[]) src).stream().map(
142+
f -> {
143+
final String path = f.getAbsolutePath();
144+
return path.contains(",") ? "\"" + path + "\"" : path;
145+
}).collect(Collectors.toList());
146+
return (T) String.join(",", result);
147+
}
148+
149+
@Override
150+
public Class<String> getOutputType() {
151+
return String.class;
152+
}
153+
154+
@Override
155+
public Class<File[]> getInputType() {
156+
return File[].class;
157+
}
158+
159+
}
160+
161+
// TODO add FileListToStringConverter
162+
}

src/main/java/org/scijava/module/DefaultModuleService.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,7 @@ public <T> void save(final ModuleItem<T> item, final T value) {
297297
return;
298298
}
299299

300-
// FIXME: Convert to string, instead of just calling toString.
301-
// Otherwise many things (e.g. File[]) are persisted improperly.
302-
final String sValue = value == null ? "" : value.toString();
300+
final String sValue = value == null ? "" : convertService.convert(value, String.class);
303301

304302
// do not persist if object cannot be converted back from a string
305303
if (!convertService.supports(sValue, item.getType())) return;

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,15 @@ public File chooseFile(final File file, final String style) {
321321
}
322322

323323
@Override
324-
public File[] chooseFiles(File[] files, FileFilter filter) {
324+
public File[] chooseFiles(File parent, File[] files, FileFilter filter, String style) {
325325
final UserInterface ui = getDefaultUI();
326-
return ui == null ? null : ui.chooseFiles(files, filter);
326+
return ui == null ? null : ui.chooseFiles(parent, files, filter, style);
327327
}
328328

329329
@Override
330-
public List<File> chooseFiles(List<File> fileList, FileFilter filter) {
330+
public List<File> chooseFiles(File parent, List<File> fileList, FileFilter filter, String style) {
331331
final UserInterface ui = getDefaultUI();
332-
return ui == null ? null : ui.chooseFiles(fileList, filter);
332+
return ui == null ? null : ui.chooseFiles(parent, fileList, filter, style);
333333
}
334334

335335
@Override

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ public void process(final Module module) {
5656
final File[] files = fileInput.getValue(module);
5757

5858
// show file chooser dialog box
59-
final File[] result = uiService.chooseFiles(files, null);
59+
// TODO decide how to create filter from style attributes
60+
// TODO retrieve parent folder??
61+
final File[] result = uiService.chooseFiles(null, files, null, fileInput.getWidgetStyle());
6062
if (result == null) {
6163
cancel("");
6264
return;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ DialogPrompt.Result showDialog(String message, String title,
304304
* @param files The initial value displayed in the file chooser prompt.
305305
* @param filter A filter allowing to restrict the choice of files
306306
*/
307-
File[] chooseFiles(File[] files, FileFilter filter);
307+
File[] chooseFiles(File parent, File[] files, FileFilter filter, String style);
308308

309309
/**
310310
* Prompts the user to select one or multiple files.
@@ -315,7 +315,7 @@ DialogPrompt.Result showDialog(String message, String title,
315315
* @param fileList The initial value displayed in the file chooser prompt.
316316
* @param filter A filter allowing to restrict the choice of files
317317
*/
318-
List<File> chooseFiles(List<File> fileList, FileFilter filter);
318+
List<File> chooseFiles(File parent, List<File> fileList, FileFilter filter, String style);
319319

320320
/**
321321
* Displays a popup context menu for the given display at the specified

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,26 +191,30 @@ default File chooseFile(String title, File file, String style) {
191191
/**
192192
* Prompts the user to choose a list of files.
193193
*
194+
* @param parent Parent folder for file selection
194195
* @param files The initial value displayed in the file chooser prompt.
195196
* @param filter A filter allowing to restrict file choice.
197+
* @param style File selection style (files, directories, or both) and optional filters
196198
* @return The selected {@link File}s chosen by the user, or null if the
197199
* user cancels the prompt.
198200
*/
199-
default File[] chooseFiles(File[] files, FileFilter filter) {
201+
default File[] chooseFiles(File parent, File[] files, FileFilter filter, String style) {
200202
throw new UnsupportedOperationException();
201203
}
202204

203205
/**
204206
* Prompts the user to choose a list of files.
205207
*
208+
* @param parent Parent folder for file selection
206209
* @param fileList The initial value displayed in the file chooser prompt.
207210
* @param filter A filter allowing to restrict file choice.
211+
* @param style File selection style (files, directories, or both) and optional filters
208212
* @return The selected {@link File}s chosen by the user, or null if the
209213
* user cancels the prompt.
210214
*/
211-
default List<File> chooseFiles(List<File> fileList, FileFilter filter) {
215+
default List<File> chooseFiles(File parent, List<File> fileList, FileFilter filter, String style) {
212216
final File[] initialFiles = fileList.toArray(new File[fileList.size()]);
213-
final File[] chosenFiles = chooseFiles(initialFiles, filter);
217+
final File[] chosenFiles = chooseFiles(parent, initialFiles, filter, style);
214218
return chosenFiles == null ? null : Arrays.asList(chosenFiles);
215219
}
216220

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363

6464
import java.io.File;
6565
import java.text.DecimalFormatSymbols;
66+
import java.util.regex.Pattern;
6667

6768
/**
6869
* Useful methods for working with {@link String}s.
@@ -80,6 +81,16 @@ private StringUtils() {
8081
// NB: prevent instantiation of utility class.
8182
}
8283

84+
/**
85+
* Splits a string only at separators outside of quotation marks ({@code "}).
86+
* Does not handle escaped quotes.
87+
*/
88+
public static String[] splitUnquoted(final String s, final String separator) {
89+
// See https://stackoverflow.com/a/1757107/1919049
90+
return s.split(Pattern.quote(separator) +
91+
"(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
92+
}
93+
8394
/** Normalizes the decimal separator for the user's locale. */
8495
public static String sanitizeDouble(String value) {
8596
value = value.replaceAll("[^0-9,\\.]", "");

src/main/java/org/scijava/widget/FileListWidget.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,24 @@
3434
import java.io.File;
3535

3636
public interface FileListWidget<U> extends InputWidget<File[], U> {
37-
// NB: No changes to interface.
37+
/**
38+
* Widget style to allow file selection only
39+
*
40+
* @see org.scijava.plugin.Parameter#style()
41+
*/
42+
String FILES_ONLY = "files";
43+
44+
/**
45+
* Widget style to allow directory selection only
46+
*
47+
* @see org.scijava.plugin.Parameter#style()
48+
*/
49+
String DIRECTORIES_ONLY = "directories";
50+
51+
/**
52+
* Widget style to allow selection of both files and directories
53+
*
54+
* @see org.scijava.plugin.Parameter#style()
55+
*/
56+
String FILES_AND_DIRECTORIES = "both";
3857
}

0 commit comments

Comments
 (0)